Is the C++ preprocessor still needed?
foonathan.net
foonathan.net
> 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.
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.
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).
If you did want to do this, you'd run a C processor on the C code first.
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.
#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.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?
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.
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...
Meaning of auto, export templates, exception specifications were the biggest ones, there a few minor ones as well.
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.
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.
out="out/make1.js"
main="make1/main.js"
gcc -C -E -H -P -nostdinc -undef -x c $main -o $out
node --harmony $out $@In the end I opted for a sed/awk/cat combo instead.
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.
http://blog.00null.net/post/144763147991/use-the-unix-m4-as-...
-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?)
It prevents #line directives which are useful for a C compiler but erroneous for javascript.
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.
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..."Of course standalone preprocessor can be (ab)used to preprocess Javascript source files and any other files, as grandparent commenter suggested.
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".
[1] https://english.stackexchange.com/questions/60154/how-to-pro... [2] http://www.stroustrup.com/bs_faq2.html#char
Disclaimer: not an English native speaker
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"
If I were consciously trying to pronounce "char" as an English speaker would, I would probably say /tʃɑr/.
I feel it's largely worked out, and is well understood.
tcc can fully compile all 6MB of sqlite in 1/100th of a second.
You are right.
BTW cpp is not slow, not compared with other parts of C++ compilers
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.
D doesn't have a preprocessor, and so that's just what it does:
BTW, __FILE__ is compiler supported magic (well, preprocessor, but they are the same thing).
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?
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.
C++ doesn't have "interfaces". No wonder you think there's too much stuff.
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!Use auto&.
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 :(
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.
Pick the right language.
I'll take my RAII and destructors, thank you very much.
And with Python you use heavily optimized native libraries (Numpy & co.) where it matters.
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.
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,...
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.
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.
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.
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”.
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}’
http://stackoverflow.com/questions/30376032/error-invalid-in...
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.
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 :)
#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.
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.In retrospect, we could have done worse, like use C++ :-).
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.
If CopperSpice was able to do it with a project as big as Qt it should be possible for any project. http://www.copperspice.com/presentations.html
I wonder two things.
- What does that even mean?
- Why is this needed when I can do well without all that in C?
It’s useless for me as well, but for different reason. I code for Windows, and Microsoft’s compiler has __interface keyword for decades: https://msdn.microsoft.com/en-us/library/50h7kwtb.aspx
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.
You can work around this using per-compiler macros, which I guess is another reason why CPP is to stay.
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++).
Maybe some of the 8-bit micros still don't..?
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! :)
The Betteridge-compliant version of this article would have been titled "Can we do without the C++ preprocessor?".