C++ won't let you define new types at runtime, but the advantage there is you know that the rest of your C++ codebase won't be doing that. It won't let you reflect on runtime data, but that means various changes you make to your program are undetectable outside of bounds (class or translation unit, sometimes shared library).
Forth or assembly will let you do whatever the hardware is game for because either can emit raw bytes and then interpret them as machine code. The cost is you have, at best, conventions about what the rest of the codebase is doing.
If you push too hard towards high performance in C++ the language lets you down. Aliasing rules mean data must be always atomic or never, and can't sometimes be an array of simd values. Or fno-strict-aliasing which costs you elsewhere. No control over calling conventions, instruction selection, register allocation or scheduling. On the bright side you can usually force partial evaluation well enough with template instantiations, but you can't have the inverse where you explicitly fold equal machine code implementations on different types. Plus trivial stuff like the embarrassment of unique_ptr imposing overhead if you pass it to a function. So C++ will get you within N% of optimal, most of the time, and that's usually good enough.
For context: I've been writing code in C++ for most of my career (25+ years) and being for VFX/3D rendering it was mostly performance critical code. Now I write Rust for some very performance critical tasks in finance ...
That’s not a thing you want from a programming language. What you can express is just as important as what you can’t.
If you've moved on then your insight is outdated.
> a group of obviously related units of which the degree and nature of the relationship is imperfectly known[0]
It's impossible for any single person to understand how the C++ language interacts with itself without a reference manual: it's grammar can lead to the most vexing parse; it has metaprogramming builtin using templates or macros, allowing for arbitrary code execution at compile time; it has a ridiculous number of ways to construct an object (move constructors, copy constructors, default constructors, is the object heap or stack allocated, are you using bracket initialization or parenthesis, etc); and more.
Also, I pointed this out in a comment awhile ago, but as of C++17 and over 20 years of writing technical books about C++, Scott Meyers doesn't trust himself to determine whether a given code snippet is or is not valid C++:
> It's not that I'm too lazy to do it. It's that in order to fix errors, I have to be able to identify them. That's something I no longer trust myself to do.[1]
If this language isn't considered complex, I don't know what is.
[0]: https://www.merriam-webster.com/dictionary/complex
[1]: https://scottmeyers.blogspot.com/2018/09/the-errata-evaluati...
That doesn't even make sense. Maybe you meant "destructors" but even then it has nothing to do with reality
I agree with your broader point but parameter packs (aka, variadic templates) were an addition to templates made in C++11. So strictly speaking they have gotten more complex.
I've found that the way I'm most productive is by using data-oriented design where I put everything in structs and don't use classes. I still use a lot of modern C++ library features, but it reads more like C.
For example:
struct BufferPool
{
alignas(PAGE_SIZE) char frames[BUF_POOL_NUM_FRAMES][PAGE_SIZE];
std::atomic_uint32_t free_frames[BUF_POOL_NUM_FRAMES];
std::atomic_uint32_t pin_count[BUF_POOL_NUM_FRAMES];
std::atomic_bool is_dirty[BUF_POOL_NUM_FRAMES];
std::atomic_uint32_t page_id_to_frame_idx[];
};
std::span<const char>
BufferPool_get_record(BufferPool* pool, RecordID rid, int db_file_fd);
What's nice about having all this stuff thrown into the language is that you're free to pick and choose what you want to adopt from it.For a long time, I tried to cram everything I wrote in C++ into an OOP pattern with classes, and it just didn't jive with me. Then I stopped doing that and started writing it more like TypeScript/Kotlin/C which I'm more familiar with and it's been a lot more pleasant ever since.
There's been so much great stuff in C++ 17/20/23 and you can just cherry pick all the bits you want to use out of it and ignore the rest.
So using one over the other doesn’t really change anything with regards to data oriented design.
I do understand that it’s just a snippet of code to show a style, so ignore this pedant!
Personally, I prefer to use C (C89) which is high-level enough to be reasonably productive, while at the same time not high-level enough to discourage the creation of overly complex code.
There are surprisingly frequent posts here from people who have written their own C compiler. I don't think I've seen a single C++ one yet.
No, come on Herb, Kate Gregory has told us why there are variables, they are names for things. The machine doesn't need names, but the human maintenance programmers do. As a Microsoft employee I'm going to hazard a guess that Herb spends a lot less time staring at C++ real humans wrote before they died/ retired/ got fired without notice than Kate does in her consulting job. She knows what she's talking about.
Without the giants aligning their interests I’m afraid there will be a split in the community. Clang’s std C++ library is already behind GCC’s because Apple and Google have “moved” on.
What re alternatives for a system programming language? (Rust seems to be fine, but still it's not super easy...)
In C/C++, the language designers can't take anything out. That would break things. There's so much legacy code.
C hasn't even totally removed "strcat", etc. yet. That should have been removed in the previous century.
And thus 'constexpr' - it can run in both modes, depending on when & where it's called.
'consteval' then is for when it must happen at compile time, which is a rather narrow (but very useful!) subset of constexpr.
Now, what is silly is that `if constexpr` is only allowed to happen at compile time. That should have been `if consteval` instead, the ordering of these additions is unfortunately reversed.
It should be explicit on what the program is going to put out, especially in a language like C/C++. When you're writing code close to the metal, you need to make sure it's exactly what you wrote. It may be a target platform difference, since I don't use the STL and mainly using C++ language features to target extremely low level devices or goals. In an application dev environment, I can see how constexpr "doing it for you" is fine, but the fact they only had one and not the other is a travesty and has so many footguns.
For example, I wrote a DRM program that required an additional buildstep to yell at the developer if they exposed strings or confidential information in the binary. This is solved by consteval, which should've been in there since the beggining.
There were already a bunch of places in the language that required compile-time evaluation- array sizes, template args, static_assert, case expressions, etc. "Can be" is all about enabling existing APIs to be used in those contexts. You can't (and don't want to) force those APIs to run exclusively at compile time, because programs are already using them at runtime all over the place!
This talk about compile time evaluation got some people thinking about all the other things they'd like to do at compile time. And there is a way to do that with just `constexpr`- `constexpr` variable initializers are a new "compile-time-only" context just like array sizes/etc, so you can decide at the leaves what to compute ahead of time, without forcing the callees to be exclusively compile time.
But there seems to be some kind of disconnect in how people learn about constexpr, where they hear "compile time" and imagine a different design than what actually went into the language, then get even more confused when they hear "can be" and imagine it as something that happens at the compiler's whim rather than deterministically based on the calling context.
Did you mean C11 there or C++11? I'm not sure why C11 is relevant in a discussion about C++. MSVC's support of C has been abysmal for ages, but this has no reflection on its C++ support. Of which it definitely supports all of C++11. And 14. And 17. And even complete support for C++20
For C++20, GCC is "only" missing module support, but otherwise supports everything else. Clang is lagging behind, though.
I like to be able to do more compile-time evaluation and it's definitely a lot less confusing now than abusing templates for that.
The comments all seem like just mindless anti-C++ circle jerking, with zero relevance to what's actually being discussed in the article. But that's sadly quite common with C++ articles here. If the title has C++, expect to see the same rants regardless of the article.
As for `constinit`, it looks like ziglang doesn't have an answer for the problem that constinit solves. That is, ziglang appears to have the same bugs here that C++ did before constinit was added.
Consider: C++ let's an increasingly large subset of the language be evaluated at compile.
Current endpoint: the whole language will have constexpr everywhere.
Correct design: Eagerly try to evaluate everything, ban things that obviously shouldn't or can't be done at compile time.
GCC has already added -fconstexpr to implicitly mark all functions as constexpr.
Where'd you see -fconstexpr?
consteval is applied to functions and asserts that a function definition can be evaluated at compile time, and only at compile time, for all possible arguments; the compiler is required to check this. This is different form contexpr that merely allows it and possibly only for a subset of the arguments.
constinit is applied to static variables and asserts that the initialization is static (static variables can otherwise still be initialized dynamically on startup or on first use).
- [1](https://www.stroustrup.com/P0977-remember-the-vasa.pdf)
Of course, it's a joke. In reality C++23 has only 20 ways to initialize a variable:
constexpr variables are constant and usable in constant expressions
constinit variables are not constant and cannot be used in constant expressions
constexpr can be applied on local automatic variables; this is not possible with constinit, which can only work on static or thread_local objects
you can have a constexpr function, but it’s not possible to have a constinit function declarationI'm a big fan of C++. It's been my primary programming language since 1996. C++11 was awesome! I'm in the minority of the C++ developers that occasionally take the language (and the compilers - just like Internet Explorer was a great pain for "front end" developers for a decade, MSVC was a pain in the a* for C++ developers) for a ride to it's limits.
However, the complexity of the language in C++ 23 is mind mind-boggling.
The complexity in C++ 11 was quite frankly already overwhelming. I've worked as a senior and principle C++ developer in small startups and big established software companies. In the last decade, I've only met a handful of really qualified C++ developers (people who actually know the language, and how to use it effectively). Most of the "Senior C++" developers I've worked with can use basic things in the same way that average Java developers can use Java - but they know nothing about for example template meta-programming or how to do concurrency correctly. Many of them were just C programmers who at some point were thrown into a "C++ project" and learned how to "use classes", but still use C functions for starting new threads. In other words, In 2022 they have not even updated themselves to C++ 11.
It's hard to learn C++ to an advanced level. It's /really/ hard. Most people don't have the personal drive to do it. At least not the majority of the C++ developers I've worked with over the years. They learn the basics, and then just use the knowledge they already have to do their job.
Even I, and friends of mine that are even more enthusiastic about C++ than I am, are starting to feel exhausted by the ever increasing complexity of the language (and the libraries). One of them suggested to me to take a look at Rust. It's a lot simpler to learn Java/Kotlin (applications) or Rust (system programming) than it is even to maintain a good knowledge about C++.
I still think C++ is a lot of fun, and programming exiting things in the language gets me into flow in a way few other activities can. It's also rewarding - I can do incredibly cool things, and I can incrementally improve on the way I solve the same problem each time I run into it in a new project.
But that's me. Most of the C++ code I've seen in small and large companies stinks. The developers have the tool to do wonderful things - but like the Wizard's Apprentice - they don't know the tool very well, and they don't know how to use it. I think most of the projects started today in C++ would be better off written in a simpler language that the developers actually comprehend.