165 comments

[ 4.9 ms ] story [ 43.1 ms ] thread
Modules change the behavior of includes
Modules are massively over-due. A faster pre-processor processor wouldn't hurt either.
Faster pre-processor? What makes you think that is the bottleneck?

tcc can fully compile all 6MB of sqlite in 1/100th of a second.

And the performance of the generated executable is....
I don't think you've understood. If it can compile the whole program, that means it must have ran the preproccesor. If it can run the preproccesor on 6MB of C in 1/100th of a second, a faster preproccesor is not going to do much for compile times since it isn't the bottleneck.
Yeah, I misunderstood you.

You are right.

SQLite is C, and C header files tend to be trivial in comparison to their template-loaded C++ counterparts. Even though Firefox is only a few orders of magnitude bigger in terms of source code it takes exponentially longer still to compile.
And you think the preproccesor is a bottleneck in C++ ?
I use the gcc preprocessor in Javascript (to avoid modules and tools like browserify):

    out="out/make1.js"
    main="make1/main.js"
    gcc -C -E -H -P -nostdinc -undef -x c $main -o $out
    node --harmony $out $@
You would be better served with m4.
I looked into m4 recently but it seems to be about the worst template engine imaginable. I'd take the c pre-processor over that any day.

In the end I opted for a sed/awk/cat combo instead.

Could you please point to some good m4 learning and reference resources?

The only time I had to write m4 was admittedly in the context of autotools, but I found it really difficult to get answers to my questions about how everything worked.

Could you please point to some good m4 learning and reference resources?

The only time I had to write m4 was admittedly in the context of autotools, but I found it really difficult to get answers to my questions about how everything worked.

looked up these flags, since I didn't know all of them:

-C: compile but do not link (I think this is covered by -E)

-E: stop after running the preprocessor

-H: print the name of each header file included, indenting child #includes (what do you use this for? is it printed to stderr?)

-P: inhibit the generation of line markers from the preprocessor (I've used this before, but can't recall the problem it solves? Not expanding __LINE__?)

-nostdinc: do not search standard system directories for header files (/usr/include, /usr/local/include, ..., this is standard in the Linux kernel, since there is no libc)

-undef: do not predefine any system-specific or GCC macros (probably to not expand occurrences of sequences like "__GNUC__" and others from unexpectedly being expanded).

-x <language>: specify the language, rather then letting the compiler guess based on the file extension (is this required?)

you mixed up -c and -C. Here he's preserving comments
> -P: inhibit the generation of line markers from the preprocessor (I've used this before, but can't recall the problem it solves? Not expanding __LINE__?)

It prevents #line directives which are useful for a C compiler but erroneous for javascript.

What the fuck! I don't know if you're a genius or just really crazy
Meh, it's just using the C PreProcessor, cpp, but driven through GCC.

The C preprocessor is pretty simple (well, until you start wanting to evaluate C expressions as part of your #ifdefs) and very well-known. It's not inherently tied to C (QED), so it makes sense. There are other cases where people use it on something other than C, but as kevin_thibedeau mentions, he'd be better served with a more powerful (and generic) macro processor such as M4.

As another abuser of the C preprocessor, I'll point out that you can greatly simplify your call to gcc... by not using gcc, just call cpp directly:

    cpp -P -H $main $out
should be equivalent, unless I'm missing something. And since somebody was asking why use -H, I'm guessing it's just for the useful verbosity of it. I've found it also causes helpful notices to be printed like "header guards may be useful for the following file..."
Yes, preprocessor is available as a standalone tool for preprocessing source files in assembly language (.S = input file before preprocessing, .s = output file after preprocessing or source file which does not require preprocessing).

Of course standalone preprocessor can be (ab)used to preprocess Javascript source files and any other files, as grandparent commenter suggested.

oh god, please don't add more stuff to the language.
Since c++11 I can't think of an enhancement that hasn't been beneficial.
Lambdas. Concepts. Ten types of casts. Templates of templates. Everything and the kitchen sink is in there.

You need a language lawyer to figure out anything but the most basic interactions. For example,

1. What's the practical difference between an abstract class and an interface?

2. When do you have a copy constructor run instead of a cast on initialization if both are defined?

3. When would you use a non-virtual destructor?

What happens when you hire people? Isn't it better to have a small language that everyone understands in its entirety, and has to go out of their way to surprise you?

> 1. What's the practical difference between an abstract class and an interface?

Interface? You are talking about the C++ language, right?

The funny thing is I just good faith assumed that this was some new feature being added in C++17 that I'd now have to figure out how to use.
> Interface? You are talking about the C++ language, right?

If Sutter's metaclasses get into C++20 C++ could indeed have an equivalent to interface enforced by the compiler.

> What's the practical difference between an abstract class and an interface?

C++ doesn't have "interfaces". No wonder you think there's too much stuff.

Ironically, Java, which does have both, has a clear and unambiguous distinction between the two.
I think that now having auto, decltype, and decltype(auto) all in the language is confusing at best. constexpr also has slightly unintuitive semantics. The problem with all of the additions to C++ isn't whether they're useful individually, it's how as a whole the language is basically a gigantic ball of special cases. C++ is extremely powerful, but also nearly impossible to understand fully.
I used to write

    for(map<int, vector<vector<int> > >::iterator it=m.being(); it != m.end(); it++)

        for(vector<vector<int> >::iterator it2 = it->second.begin(); it2 != it->second.end(); it2++) 


Now I have to write

    for (auto paths : m) {

        for (auto path : paths) {


Sure clang annoys me with not being able to compile with -g, but still auto is a lifesaver!
This is neither here nor there, but in your revised version you are copying all the vectors many times.

Use auto&.

While perfectly fine, this comment unironically reinforces the GP's assertion of "how [C++] as a whole the language is basically a gigantic ball of special cases"
What specifically annoys me about C++ and is demonstrated by this example is that the simplest possible expression of an algorithm is almost certainly not the most efficient or desirable way to do what you are trying to accomplish.

In a language designed for humans, the simplest expression of a loop (e.g. `for (auto paths : m) {}`) would use references not value copying. You'd have to go out of your way to do the it the dumb way, which you would only do when you have a specific reason to do so.

C++, on the other hand, is only really an effective language in the hands of people who have either fully internalized the 1,000 page specification, or wasted a depressing number of synaptic connections on memorizing minute trivia about what constructs to use under what circumstances.

I say this as someone who works with C++ on a daily basis :(

> C++, on the other hand, is only really an effective language in the hands of people who have either fully internalized the 1,000 page specification, or wasted a depressing number of synaptic connections on memorizing minute trivia about what constructs to use under what circumstances.

No. You use profiling tools and optimize hotspots. Pareto principle applies here so you look at 20% of program tops.

Rest is irrelevant and can be executed as inefficiently as you please as long as you got your big O complexity right.

Or the language and compiler can just save you the trouble and get it right from the beginning.
What you see as trouble other people see as desired behavior.

Pick the right language.

Isn't C++ still pretty much the fastest language out there, except Fortran for some numerical applications? I thought that's the main reason to use it over Python/Java/etc.
C. Use C.
Having recently done some C development after a few years of exclusively C++, no, no thank you.

I'll take my RAII and destructors, thank you very much.

Hence why already back in 1993, when faced with the option of Turbo C 2.0 vs Turbo C++ 1.0, I could not understand why bother with C at all, still don't.
Those language creators and their names, "C","C++", "Objective-C","HolyC", "C#". I almost thought "C." was a new language.
Who said anything about Python or Java? In any case Java and C++ are roughly similar. The next tier of languages such as Rust or Haskell are within a small constant factor in the usual benchmarks. But quoting such benchmarks is questionable because the C and C++ implementations require much more man-hours of hyper optimization to get into that top-tier position. The real important takeaway is that the first or second attempt in C++, by someone who is not a language lawyer, is nowhere near the maximum achievable speed. Whereas the first or second attempt by a similar layman in a reasonable language like Haskell is not only faster than the initial C++ version, but has fewer bugs as well.
> Who said anything about Python or Java? In any case Java and C++ are roughly similar.

And with Python you use heavily optimized native libraries (Numpy & co.) where it matters.

Speaking as someone who has spent time optimizing programs in both languages, Rust is certainly not in the same tier as Haskell. It should be in the same tier as C or C++. If there's a program where it's not, then we consider it a bug. (And indeed, those exist. :-) But they are fixable!)
C++ and Java are absolutely not similar in terms of performance. Java requires a virtual machine, C++ compiles to binary. Not in the same league.

And in regard to the first draft of a C++ program being slow until you spend lots of time optimizing it, when comparing to say Haskell, that is not at all my experience. I'm generally able to write clean and efficient programs in C++ on the first draft, and they are invariably more performant than if I had written it in a slower language, without a significantly greater amount of effort.

Java doesn't require a VM per se, there are plenty of commercial JDKs that compile straight into native code.

In case you want to state that a VM is like a language runtime in that case, C++ also needs one for handling exceptions, RTTI, global constructors/destructors, floating point emulation,...

I used to despise C++... but now I work on it every day, and while I don't think it's great, it is not so bad. Turns out my hatred was entirely for the std library. At my work, we simply don't use it. And thus `auto` hardly saves anything, so we don't use that either, and I don't miss any of it.
> In a language designed for humans, the simplest expression of a loop (e.g. `for (auto paths : m) {}`) would use references not value copying.

In C++ values are first class, making 'auto x' an implicit reference would be surprising for anybody with experience with the language model. It is also consistent[1] with the template deduction rules. I strongly believe that not not having first class values in a language with mutation is a design mistake [2].

[1] IIRC the ill designed initialization_list has special rules for auto. [2] C++ references are not first class which is also a mistake.

> In a language designed for humans, the simplest expression of a loop (e.g. `for (auto paths : m) {}`) would use references not value copying. You'd have to go out of your way to do the it the dumb way, which you would only do when you have a specific reason to do so.

But I would expect

    auto x = y;
to create a new variable by copying with type-deduction from y, not a reference (or even const reference) to y. If I want a reference anywhere in the language I always have to say so explicitly. As such, I find it perfectly normal that

    for(auto x : y)
create a copy of the elements of y while

    for(auto& x : y)
creates a reference to the elements of y. Reversing this just within for-loops would only add to the confusion. Defaulting to references over values just for auto where everywhere else values are the default and references need to be specified would also only add to the confusion.

Of course you may now say that one should have done it right from the start, but this is then an argument about the initial decisions in the late 80s, not about the use of auto in C++11.

Regarding the specification, I am incredibly happy that there is a specification I can trust and nowadays a series of different compilers which largely implement this specification instead of some "model-implementation" which implicitly defines the language and may look entirely different next week.

> But I would expect "auto x = y;" to create a new variable by copying

Why would you expect that? There are plenty of examples of other referentially transparent languages where "x = y" means x and y refer to the same thing, not different things (one newly created) initialized to the same value. Maybe you expect it to work the way it does because that more closely resembles how C++ has worked for you in the past, which is a bit of a circular argument.

> Of course you may now say that one should have done it right from the start, but this is then an argument about the initial decisions in the late 80s, not about the use of auto in C++11.

Yes, that's exactly the point I am making.

Are you sure you're making a point – because I can't see one. Seems more about unnecessary bitching about C++ being different from [some-different-language].

If they could do anything more stupid as introducing a galling difference and a special case to variable initialisation with auto, it would be to do exactly that, but because “some other languages work more like that”.

Tried this on a vector<bool>, got this error:

error: invalid initialization of non-const reference of type ‘std::_Bit_reference&’ from an rvalue of type ‘std::_Bit_iterator::reference {aka std::_Bit_reference}’

Does const auto& work though?
I normally use for(auto&& x : ...) which works with those rare containers that have proxy references, while still allowing mutation.
Heh, when I first heard that C++ vectors were bitvectors I was like "that can't be right? You can't get sensible references to members that way!" But it turns out that C++ is fine with breaking parametricity so references to bitvector elements just don't compile.
I wouldn't say C++ is fine with it. vector<bool> is widely viewed as a mistake. I would actually say it's one of the few inconsistencies in a standard library that tends to be very consistent.

The problem is that (quite rightly) the C++ standard committee tries very hard to not break existing code. There isn't really an easy way to deprecate a specialization of this container without causing 32x more memory use if someone was relying on it, and even worse it would be a silent breaking change.

You mean 8x more memory, right?
I mean, this isn't the only case of broken parametricity I've come across in C++ codebases. Though it's the only one in the stdlib.

I know vector<bool> is controversial (and I understand it can't be "fixed" now), I was just surprised that something breaking parametricity so blatantly was ever agreed upon in the first place -- in more functional languages parametricity is kinda much more jealously guarded. Bit of a culture shock there, is all :)

Yep, the "auto" keyword was added because even the inventors of the template system had a understanding of the syntax they created.
I can't wait to see modules, namespaces, and preprocessor include guards all working together in the same application.
The year is 2017 - is anyone worried about the preprocessor?

I feel it's largely worked out, and is well understood.

The year is 2017 - is anyone worried about the preprocessor?

I feel it's largely worked out, and is well understood. I

You would break many libraries starting by boost.
I have to comment on this, even though it's almost completely irrelevant to the blog post. I saw "do_sth" so I wanted to check where the writer was from. Germany. Aha. :-)

I love how reliably abbreviating "something" (e.g. "sth", "smth" or "sthg") indicates that the person isn't from an English-speaking country. I wonder why this abbreviation has never caught on in English-speaking countries. I think the abbreviations got popularised in non-English-speaking countries by English dictionaries.

I think the more common idiomatic "abbreviation" would be "do_stuff" when you don't want to type out or say "something".

I'm sure it's not valid across the board, but most of the internet shortenings that we use in english are acronyms---lol, stfu, wtf, rofl, hbu, asl, etc. I'm actually having a hard time thinking of some common abbreviations. I guess "func" short for "function" is a common example.
Programming has plenty of common abbreviations: obj, str, char, btn
Plus old programmers had to pay a heavy vowel tax so many, even unnecessary, abbreviations stick with us. I worked with an older guy on a game once, "extern struct CollisionManager g_clisn_mngr" is an example of the sort of things we did...
(comment deleted)
Because as you point out yourself, there really is no need for that abbreviation. You could say "do_stuff" and it would have approximately the same meaning, but not be an undecipherable, ambiguous abbreviation.
Yes, but it's not the only occasion it's being used. "That must be a bug or sth" is another possibility, for example.
That "or sth" seems entirely superfluous.
"That must be a bug, or the result of hardware failure, or an erroneous report from the user, or maybe there's some other possibility I haven't thought of."
That would be "something else." The else is important.
And implicit when someone ends a sentence with "or something".
Similarly OT, a pet peeve of mine is how Germans and others consistently pronounce (the eg. SQL datatype) VARCHAR as var-CHAIR or sometimes var-CZAR (war czar, warczaw) when they pronounce CHAR/CHR correctly.
Out of interest, what do you consider to be the correct pronunciation of CHAR? There seems to be considerable variation in how people pronounce it (see e.g. [1,2]). Personally I have always used the '/tʃɑr/' one, and similar for VARCHAR.

[1] https://english.stackexchange.com/questions/60154/how-to-pro... [2] http://www.stroustrup.com/bs_faq2.html#char

As in "character" for which CHAR and VARCHAR are abbreviations?

Disclaimer: not an English native speaker

We're going with VAR-care and care as correct pronunciations of both, right? Asked as a native US English speaker.
I've never heard it pronounced that way, although 'care' is how the first syllable in the word 'character' is pronounced.

I've always heard it pronounced var char (the parts rhyme). Char is pronounced like when you burn/blacken a steak. "Char-grilled food is cooked over or under direct heat so that its surface becomes slightly black"

Logic dictates it should be pronounced as char-acter, but my brain brings out char-coal always.
(comment deleted)
(comment deleted)
That's because the English language is a mess with respect as to how something is pronounced.
Great point! The words... their, mare, hair, where... all rhyme in English. This drives Spanish speakers crazy.
Or because it behaves just like a pidgin..
(comment deleted)
I'm Dutch, so I pronounce it with a hard ch as in loch, even when speaking English.
If I'm understanding you correctly, then most English speakers would call that correct. It's short for "character" so is pronounced as the first syllable of that (with a hard 'ch') not like the word "char" is pronounced. I think I personally would swap between the two fairly randomly, probably due to learning to code from text-based sources and not hearing it pronounced till much later.
To be clear: I do not mean the /k/ sound, but the /x/ or /χ/ (voiceless velar/uvular fricative), which is seldom used in English, except when properly pronouncing the names of Scottish lochs, or the name of J.S. Bach. Maybe it's not even used at all in English, but only in Scottish, but we're getting off-topic already.

If I were consciously trying to pronounce "char" as an English speaker would, I would probably say /tʃɑr/.

Interesting; I pronounce "loch" as "lock" and Merriam-Webster seems to agree. https://www.merriam-webster.com/dictionary/loch I guess you're referring to the UK pronounciation listed here instead? http://dictionary.cambridge.org/us/pronunciation/english/loc...
(comment deleted)
Funny you should say that. I'm from an English-speaking country and I use "sth" a lot.
I'm a native anglophone living in the UK. I abbreviate "something" as "sthg" sometimes. I wouldn't do it in a function/variable/method/class/... name, but then I don't think I'd use "something" as part of such a name in the first place in anything I wasn't very confident would only ever be throwaway code.
Given the timestamps of the two English respondents in favour of "sth", I am tempted to amend my hypothesis to say it's a European thing, and more predominant in continental Europe.
C coder here (former pre-C++11 C++ as well).

Another point to mention (which I think adds to the conclusion) is that by losing compatibility with the C preprocessor you'd lose compatibility with, well, C.

This means breaking of both C++ code that uses C libraries legitimately and "C++" code that's actually C with new/delete (or "the horror" as we put it).

True, and the worth remembering.

If you did want to do this, you'd run a C processor on the C code first.

I would also add that when you're doing low-level work that requires mixing a bit of assembly and C (Or C++, but I mainly stick to C), being able to access the preprocessor from assembly is really nice. The alternative is redefining tons of stuff so you can use it in your assembly. It's a niche use-case obviously, but it is still a nice use-case regardless and I'd wager you could do this with lots of other languages if you ever had a want/need.
(comment deleted)
> by losing compatibility with the C preprocessor you'd lose compatibility with, well, C.

Thankfully C++ no longer needs it to gain market share, it can stand on its own, while cleaning up the warts that caused unsafe code as a means of getting adopted.

Ha ha ha,

  #ifdef __cplusplus
  extern "C" {
  #endif
I guess you will have to rewrite libc.so and ntdll.dll in C++ first. Or at least fork them and maintain the fork indefinitely.
They are already written in C++, pay attention to the news!

https://blogs.msdn.microsoft.com/vcblog/2014/06/10/the-great...

"We have converted most of the CRT sources to compile as C++, enabling us to replace many ugly C idioms with simpler and more advanced C++ constructs. The publicly callable functions are still declared as C functions, of course (extern "C" in C++), so they can still be called from C. But internally we now take full advantage of the C++ language and its many useful features."

Note the reference to ugly C idioms.

Also all major C compilers are written in C++, including clang and gcc, did you miss that as well?

Seeing "replace ugly C idioms" my first reaction was: "why didn't they use "nice C idioms instead?".
Because nice C idioms is an oxymoron.
CRT != ntdll.dll and CRT != kernel32.dll.

CRT is an optional component of Windows software, a layer above ntdll and kernel32. Anyone can easily compile a program without C runtime library using /Zl compiler option. In this case, only Win32 and native API functions will be available, but not functions like printf() or strcmp().

So latest versions of CRT could be rewritten in C++, but this has nothing to do with documented OS API or even low-level undocumented native API, which are both still pure C.

The compiler switch /kernel was introduced with Visual Studio 2012 (Windows 8).

https://msdn.microsoft.com/en-us/library/jj620896(v=vs.110)....

C code is considered legacy by Microsoft with C++ being the official systems programming language, hence why they dropped support for newer C versions, except for what is required by ANSI C++ standard.

https://herbsutter.com/2012/05/03/reader-qa-what-about-vc-an...

Our focus in Visual C++ is on making a world-class C++ compiler

Our primary goal is to support "most of C99/C11 that is a subset of ISO C++98/C++11."

We do not plan to support ISO C features that are not part of either C90 or ISO C++.

Also the future of Windows lies on UWP, and C is certainly not a way of writing UWP APIs, unless one enjoys low level COM Windows 95 style.

Finally, the remaining C code is slowly being migrated to C++

https://www.reddit.com/r/cpp/comments/4oruo1/windows_10_code...

I don't think C++ will ever shrink as a language (i.e., drop features), but we can reduce the learning surface for newcomers by making it simpler, more consistent, and relegating C-isms under the "backwards compatibility"umbrella.
C++11, C++14 and C++17 have dropped features, now there is even a zombie keywords section on the standard.

Meaning of auto, export templates, exception specifications were the biggest ones, there a few minor ones as well.

Maybe dropping compatibility with C is a good thing?

All other languages wanting to interface with C seem to be using libffi or something. Currently C++ gets a shortcut, but in the future maybe it could go through an FFI.

It'd still be compatible with C from libraries, just not source. Those libraries would have to ship header files free of macros of course.

I think you're advocating for dropping compatability with a little under half of all c++ projects.
I don't see how losing backwards compatibility would be a good thing per se. Even if the preprocessor were considered a problem (which is disputed), it could coexist with a new FFI system that could be implemented.

Losing backwards compatibility is usually a price to pay, not an objective. As an example, Python 3k chose to lose partial compatibility to 2.7 in order to change some parts of the language. While the user and maintainer community still debates if the change itself was a good decision (a different discussion IMO...), probably most will agree that the incompatibility bit has been and will be for some time a source of pain.

I think a more accurate version of Betteridge's law says: when a headline asks a question, the answer is always the less surprising answer. It just happens that usually the more attention-grabbing way to write the headline is the one that makes "no" the less surprising answer.

The Betteridge-compliant version of this article would have been titled "Can we do without the C++ preprocessor?".

(comment deleted)
I mean no offence but looking on what this guy says he's probably never worked a paid C++ job and it looks like he will never be.
If you want to keep compiling any existing C++ code, then the answer is "yes".
After reading about all the hoops the author is willing to jump through to avoid using the preprocessor, I was expecting the conclusion to be, yes, it turns out we still need it.
The conclusion is "yes, we still need it".
> The function std::experimental::source_location::current() expands to the information about the source file at the point of writing it.

Hmm, okay, and what happens when someone puts that thing in a macro?

Some macros can be horribly ugly, but I'm not sure replacing __FILE__ with compiler-supported magic is an improvement.

The use case for source_location is as a default value of a (function or template) parameter.

BTW, __FILE__ is compiler supported magic (well, preprocessor, but they are the same thing).

(comment deleted)
Also, with #pragma once (practically supported by every compiler toolchain) we don't need to do the if-not-defined-include trick.
Not if you do embedded.
What does "embedded" have to do with anything? #pragma once works fine in gcc-arm-none-eabi.
I think he means you may not have #pragma once available in every embedded compiler. Embedded compilers are a mixed bag; gcc is a standout.
Yes there is world out there outside of gcc sphere ;)
Even ARM's embedded cortex-m compilers have supported #pragma once for years now.

Maybe some of the 8-bit micros still don't..?

Didn't you ever have to program a Persistor 68332 with an ancient version of CodeWarrior? How about CCS C for the 18F PIC?
Thankfully no, M0s nearly come in cracker jack boxes now days. A lot of what used to be on 8bits are moving to M0s because why the heck not.

Commercially advertised prices are around 60 cents in bulk, corporate buys in the 100s of thousands are going to be cheaper than that. Death to 8bits! :)

FWIW we had issues with pragma once, MSVC and symlinks.
(comment deleted)
Well until C++ develops an implementation of schemes syntax-rules - I hope it stays.
I am missing the motivation to purge the preprocessor - only reason he gives is the paradigm that the preprocessor is considered bad practice. I fail to see why. The preprocessor is a useful tool that can save you time and make code more readable and portable. Conditions at compile time guarantee that the compiler is not subjected to a specific portion of code, and it offers the only way to define a constant value without having an implicit type for that. Considering the use of C/C++ outside pure computer programming, like embedded systems or digital signal processing, where portability, code size and target specific substitutions or optimisations matter, there is simply no reason and no way to get rid of the preprocessor without breaking things. Sure you can use it in obscure and wrong ways, but it is not the job of the language to act as a nanny and prevent you from doing evil by restricting access to dangerous toys.
CPP is not without warts. It complicates static analysis (machine and human), for one thing. You need the definition of variable foo or function bar? Great, which one? Who made the last assignment to foo and what its value could be? Does this ifdefed snippet here count?
It is also not without very legitimate uses. Feel free to not use the preprocessor if you don't want (you're still using it with `#include` though), but it is very useful for specific use cases, and there is no real reason to remove support for it.
Author here, I didn't intend to give a motivation for purging it, many people in the C++ community want to and I wanted to explore the feasibility of that in the current status.

> like embedded systems or digital signal processing, where portability, code size and target specific substitutions or optimisations matter, there is simply no reason and no way to get rid of the preprocessor without breaking things.

It certainly is, I know embedded projects where they use template meta programming to get an even more advanced code generator.

(comment deleted)
The post seems to be more about evaluating alternatives to preprocessor-based solutions to common problems; even when alternatives exist, reasonable people can disagree about whether they're actually better, but I think most would agree that it would be nice to have a proper module system instead of using textual includes, or that the fact that macros cannot be namespaced and can lead to ODR violations is not awesome.

Anyway, the post ends saying

> With current C++(17), most of the preprocessor use can’t be replaced easily.

and I think we can all agree on that.

The thing is all people don't use C++ the same way - I've often found myself using a small amount of C++ mixed with large amounts of C written by others - my use of C++ needs to live with other's C headers, their #defines and #includes - my use of C++ is no more or less valid than your's

BTW cpp is not slow, not compared with other parts of C++ compilers

> don’t use it for inline functions

That's a myth afaik. Is there any standard way besides macros to really ensure the code will be inlined? In particular, the suggestively named inline keyword just doesn't work like that.

There are attributes like `[[gnu::always_inline]]` for GCC and clang.
Yes, but they aren't portable.

You can work around this using per-compiler macros, which I guess is another reason why CPP is to stay.

The preprocessor is a workable but somewhat poor tool for inlining code due to several reasons (no overloading, no sensible macro scoping, template argument lists clash with macro argument lists, etc). However, it's quite a reasonable tool for helping to force inline code in a nonstandard but somewhat portable way. See, for example, EIGEN_ALWAYS_INLINE at https://eigen.tuxfamily.org/dox/Macros_8h_source.html
Sticking with stuff that's in the standard, "constexpr" is, among other things, a more strongly-suggested "inline".

Also, inlining is likely if you compile with -02/-03 and declare the function with internal visibility (in an anonymous namespace or with "static", although the latter is deprecated in favor of the former in C++).

OCaml doesn't have a C-style preprocessor, but I sometimes wish it did have one. One case is where a function has changed its type signature, eg. adding a parameter, but you still want to be able to write code that can compile with the old and new versions of the library. It would be quickest to write:

    #if HAVE_OLD_LIB
        f 1 2;
    #else
        f 1 2 3;
    #endif
The actual solution in pure OCaml is a huge pain. Usually you have to add another module which is conditionally compiled.

Luckily in OCaml you can write ‘ocamlopt -pp cpp’ to use the C preprocessor on selected files.

Yes, but I don't know if you're talking about the C++ (templates and constexpr's & whatnot) or the C preprocessor. Macros? Get rid of those and you kill my logging solution.
I'm wondering if anyone has tried to use another language - I'm thinking Ruby - as a pre-processor for C++ code. I know people have pre-processed C++ code with various tools - for example Qt - but I'm thinking of just replacing # pre-processor statements with something like Rails based templates.
I used Python to generate C++ with a utility I wrote called "PythonPP": http://matracas.org/utiles/index.html.en

One of the things it did was to generate the function prototypes so that a typical .h file would look like this:

  #!from base_cc import *
  #""// Clase:    ${project_part_name}
  #""// Proyecto: ${project_name}
  #!import time
  #""// Fecha:    ${time.strftime("%A, %d de %B de %Y, %H:%M:%S %Z", time.localtime(time.time()))}
  #""${cc_copyright_notice("GPL", "2001, 2002, 2003, 2007, 2010")}

  #""#ifndef _H_${project_part_name}
  #""#define _H_${project_part_name}

  #""${cc_function_includes_for_class()}
  #""${cc_variable_global_declarations_for_class()}

  #""class ${project_part_name}
  {
  #""${cc_variable_declarations_for_class()}
  #""${cc_function_protos_for_class()}
  };

  #""${cc_operator_protos_for_class()}

  #endif
The .cc file was similar, calling Python functions to get the function definitions from separate files: I built one directory for each class, where I would put each big function or several related small functions in its own file, and they would be found and combined automatically.

For each class, there was a vars.cc file which contained member variables, with their declaration line, initialization and destruction in consecutive lines: the declaration went to the .h file, the initialization to the constructor, and the destruction to the destructor. This way I avoided forgetting to release something and had a good overview of all variables and their life cycles.

Here is an example of generating constant names from a Python list:

  #!for token_id in lexer_table:
  #"  "            case TOKEN_ID_${token_id[1]}:
  #'  '              cerr << "\n   --- [" << index_of_current_character
  #"  "                   << "] TOKEN_ID_${token_id[1]} '" << buffer << "'"
  #'  '                   << endl << flush;
  #'  '              break;
The same idea was also used in the Bison/Yacc grammar file:

  #!%token <markup> T_QMATH_DIRECTIVE
  %token <markup> T_DIRECTIVE_DEFINITION
  %token <markup> T_DIRECTIVE_CONTEXT
  for d in qmath_directives:
  #"  "%token <markup> T_DIRECTIVE_${d.upper()}

I've used it also for other languages like Java where it cuts down the boilerplate. For instance, the package names are automatically generated from the file path.
I've seen it done (on a large-ish codebase), using Perl as a preprocessor for C. It looked good on paper and we were really enthusiastic when we came up with the idea, but it quickly degenerated into the proverbial ad-hoc, informally-specified, bug-ridden, slow implementation of half of Common Lisp.

In retrospect, we could have done worse, like use C++ :-).

A while ago, in a fit of defmacro-envy, I wrote a preprocessor that would let you wrap Python snippets in #python/#endpython blocks: http://codepad.org/5B388Wsh. This lets you write silly things like http://codepad.org/eS443WPA. I bet the same idea isn't too hard to implement in Ruby, but I don't know enough to try it myself.

I never really used it much - generating C code with Python print statements turned out to look uglier than I thought it would, and most of my use cases were better handled with X macros (https://en.wikipedia.org/wiki/X_Macro) or by generating a separate file and #include'ing it. But your description reminded me of it; you might like the idea.

sometimes I'd use C macro just because it's so darn hard to write capturing lambda code in C++. But capture lambda is suppose to be better and safer. I should use it more.
When I do that, I just type [](){} first, then start filling whatever I need in between those things.