I feel like C++ is moving towards being as useful as a brick. You get a brick and you ask yourself "what can I do with it", and the answer is "everything! absolutely anything!" Then you ask yourself "how?" and the answer is 3000 pages of standards, activities and rules on how to actually use a brick to make truly anything, with 800-page addenums to using boost-brick (i.e. some mortar).
If it's too difficult for you to use, then use another language which holds your hand more. But for many of us, it's an incredibly powerful language and there isn't really anything comparable when you need to squeeze every instruction cycle out of the CPU while still using HLL features. I don't find it difficult at all to use. Sure it's complicated, but not everything worth doing is trivially simple.
Agreed, but, sometimes people need to do stuff RFN. And sometimes that doesn't leave time for learning new things, even if they might be somewhat better.
And edit to add that I think rust, in particular, is doing some very interesting things to hugely reduce the learning curve and make it a better language for newcomers.
I pretty much only program in C++ at work and Rust at home now, and I think Rust is hands-down a better designed language. It just feels more coherent and consistent.
I used to despise C++, but ever since I've had to abandon all usage of it's std library, I don't mind it anymore. It's a bit less crufty that way. Though I'm still peeved every time the compiler dumps 30 pages of every virtual method in existence because it couldn't find one that matched my call.
> I pretty much only program in C++ at work and Rust at home now, and I think Rust is hands-down a better designed language. It just feels more coherent and consistent.
C++ turned out kinda... good -- considering that it started out as "tacking classes on C".
Classes (with destructors) is the single most powerful and important addition to plain C. In C, even exceptions can be emulated, more or less naturally, but destructors cannot (without a compiler-specific extension). So, I'd argue that the best parts were the ones that came in early. The latest version of C++, of course, is nothing to sneeze at, either.
I agree with you. If you can't get the job done using mostly that language described in the Brown Book (C++ Annotated Reference Manual by B. Stroustrup and Margaret Ellis), plus a judicious smattering of some newer C++ ranging from C++98..C++03 (at most), you should fucking retire.
The last useful change in C++ was (circa 2006?) making string literals const. That's it.
What about: lambdas, type inference, tuples, robust templates, compile time constants and code execution, move semantics, expanded PoD, initializer lists, range based for loops, user literals, return type deduction... I could go on and on. All features I use daily. And I'm not even getting into C++17.
Half-broken, bolted-on garbage that is found in a much nicer form in high level languages which have those things in their core vision from the beginning, or the kind of extensibility which can cleanly support new ideas.
You think rvalue references are a natural and elegant construct? That if C++ were designed with move semantics in mind, this is the road they would have travelled down?
That's the biggest reason that I like Rust more than C++, even more than lifetime/safety. I've been working on a C++14 application, and "std::move" shows up a lot more than implicit copies, which not only makes the code more verbose, but analyzing performance more difficult. (I so wish I could be using Rust, but, unfortunately, the only FOSS DNP3 implementation is in C++.)
Please show me the "nice" and "well thought" and "elegant" and "modern" language with the sheer amount of resources, documentation and momentum that C++ has? It's not going to go away for the foreseeable future, even if slightly better options arise (notice the slightly, no language has substantially improved on C++ in a meaningful enough way to make the benefits of migrating greater than the headaches).
Here is another thing: each time they add this or that cruft to C++, the syntax for that tidbit has to go into the hard-coded grammar. For the past 30 years, they have been missing opportunity after opportunity to somehow make the language internally extensible, so that new rules don't have to be added any more to a hard-coded parser (but, say, defined by C++ programs themselves). So now, check this out, GNU C++ has a hand-written recursive-descent parser ... that is 1195625 characters of C. It's almost 40K lines just to scan some tokens and get an abstract syntax tree.
At this point, is there any parser out there that could deal with C++'s grammar?
What you're suggesting is tantamount to throwing out C++'s current syntax, which will never happen, or taking the existing parser and making it even more complicated so that it supports extensions.
IIRC g++ had historically a machine generated parser (bison maybe?) But it got replaced around 3.0 time because the new hand written parser was faster and more maintainable.
I think, you cannot beat beats shift-reduce LALR(1) with recursive descent on raw performance on a reasonable syntax. However, parsing C++ with LALR(1) will require unmaintainable hacks in the grammar. Recursive descent accomodates ad-hoc fudging much better than parser generation. In any given rule, you can look ahead as much as you want and invoke any Turing computation to decide the parse. Also, recursive descent approaches support backtracking fairly naturally: try it this way, as far ahead as necessary, or else start again, falling back on this. Let's see ...
28374 /* Commit to the currently active tentative parse. */
28375
28376 static void
28377 cp_parser_commit_to_tentative_parse (cp_parser* parser)
28378 {
28379 cp_parser_context *context;
28380 cp_lexer *lexer;
28381
28382 /* Mark all of the levels as committed. */
28383 lexer = parser->lexer;
28384 for (context = parser->context; context->next; context = context->next)
28385 {
28386 if (context->status == CP_PARSER_STATUS_KIND_COMMITTED)
28387 break;
28388 context->status = CP_PARSER_STATUS_KIND_COMMITTED;
28389 while (!cp_lexer_saving_tokens (lexer))
28390 lexer = lexer->next;
28391 cp_lexer_commit_tokens (lexer);
28392 }
28393 }
Bison supports GLR ("generalized LR") via some technique of forking the parser and trying alternatives in parallel, which seems related to this.
The problem with C++ grammar is that inside templates, things are too flexible at times. For example:
template<bool> struct a_t;
template<> struct a_t<true> {
template<int> struct b {};
};
template<> struct a_t<false> {
enum { b };
};
typedef a_t<sizeof(void*)==sizeof(int)> a;
enum { c, d };
int main() {
a::b<c>d; // declaration or expression?
}
It's a fun C++ IDE torture test, which few pass. One that does is Visual Studio - if you create a C++ project and set up both 32-bit and 64-bit build targets, you can flip between them, and see the syntax highlighting change on the line with the comment.
I found the README confusing. Basically it appears to compile C -> ELVM IR all in C++ constexpr. Doing this requires preprocessing the input file into a string inside the C++ source file. The result is that ELVM IR is embedded as a string inside the output binary, and running the output binary does nothing but dump this compile-time-computed string.
Am I correct in understanding that, contrary to the Turing completeness of C++ template instantiations, the (compile time) computational power of these Constant Expressions thing is intentional?
Yes. A "constexpr" function can use a large subset of C++, including calls to other constexpr functions, but the compiler guarantees to evaluate it at compile time, so you can use it where the compiler expects a compile-time constant.
> the compiler guarantees to evaluate it at compile time
It doesn't guarantee that.
For example:
static constexpr int fac(int x) { return x <= 1 ? 1 : x * fac(x - 1); }
int f() {
return fac(4); // May or may not be evaluated at run-time.
}
char arr[fac(4)]; // fac(4) is evaluated at compile-time.
I should have said, "guarantees that it can evaluate it at compile time if needed, such that you can use it where the compiler expects a compile-time constant expression". Yes, the compiler can choose to defer it until runtime in some cases.
Yes, among other things. As long as a program can't tell the difference (as per the as-if rule), the compiler is free to delay tjee evaluation till runtime.
Much saner than abusing templates for this purpose. Faster, better error messages, and miles more readable. I look forward to the day when all the metaprogramming inside boost is rewritten to use constexpr.
Metaprogramming is different than compile time programming. I agree with templates being abused as a compile time programming language but they are used as Metaprogramming just fine (well, or not but that's their primary usage)
I discussed this very thing with Stroustrup as far back as the mid 1990s. He was, for example, a big fan of Lisp's macro capabilities, and described templating as a way of providing this kind of capability while (back then it was a goal) the ability to compile C.
(Compiling C is mostly out the window now of course)
Why do you say that compiling C is "mostly out the window"? It's difficult to imagine how C++ could change in a way that broke C compatibility without also breaking compatibility with a great deal of C++ code.
C++ isn't compatible C, it never was. There's lots of valid C code that isn't valid C++. For instance, this line:
int *new = malloc(sizeof(int));
is valid C but invalid C++ for two different reasons: "new" is a keyword in C++, so you can't use it as an identifier, and C++ doesn't allow implicit casts from void*, while C does.
Oh, sure, and the void pointer thing, etc., but that's all been true from the start. From gumby's comment I got the impression that there might be some more recent trend toward abandoning C compilation, and that's what I have a hard time imagining.
For years, Microsoft's C compiler only supported C89 and would choke on C99 code (that used C99 features). That has recently changed (I'm guessing with the release of C11, which made certain C99 features optional instead of mandatory) but for a long time, it seemed that Microsoft was saying "We don't care about C."
It's true that the C circle in the Venn diagram leaks out of the C++ circle a bit, but I find in practice most of it is contained quite well inside the C++ one.
My current hobby project is a language VM that compiles cleanly as both C and C++. It took very little effort to get it working in C++ despite hacking on it for months as a strictly C project first.
This C compiler is compiled at the same time the C compiler compiles your program. In other words, it compiles C at compiler compile time, where with other C compilers the program's compile time is the compiler's run time.
No, the C compiler executable compiles your program at run time, i.e. when the compiler is running. This C compiler compiles your program while the compiler is being compiled (well, you never end up with a compiler executable, just your compiled program).
Lightweight Modular Staging (Scala): https://scala-lms.github.io/
Terra (Lua): http://terralang.org/
Compiler Plugins (Rust): https://doc.rust-lang.org/book/compiler-plugins.html