Orthodox C++ (sometimes referred as C+) is minimal subset of C++ that improves C, but avoids all unnecessary things from so called Modern C++. It's exactly opposite of what Modern C++ suppose to be.
Back in late 1990 we were also modern-at-the-time C++ hipsters, and we used latest features. We told everyone also they should use those features too. Over time we learned it's unnecesary to use some language features just because they are there, or features we used proved to be bad (like RTTI, exceptions, and streams), or it backfired by unnecessary code complexity. If you think this is nonsense, just wait few more years and you'll hate Modern C++ too ("Why I don't spend time with Modern C++ anymore" archived LinkedIn article).
Code base written with Orthodox C++ limitations will be easer to understand, simpler, and it will build with older compilers. Projects written in Orthodox C++ subset will be more acceptable by other C++ projects because subset used by Orthodox C++ is unlikely to violate adopter's C++ subset preferences.
#include <stdio.h>
int main()
{
printf("hello, world\n");
return 0;
}
- C-like C++ is good start, if code doesn't require more complexity don't add unnecessary C++ complexities. In general case code should be readable to anyone who is familiar with C language.
- Don't do this, the end of "design rationale" in Orthodox C++ should be immedately after "Quite simple, and it is usable. EOF".
- Don't use exceptions.
Exception handling is the only C++ language feature which requires significant support from a complex runtime system, and it's the only C++ feature that has a runtime cost even if you don't use it – sometimes as additional hidden code at every object construction, destruction, and try block entry/exit, and always by limiting what the compiler's optimizer can do, often quite significantly. Yet C++ exception specifications are not enforced at compile time anyway, so you don't even get to know that you didn't forget to handle some error case! And on a stylistic note, the exception style of error handling doesn't mesh very well with the C style of error return codes, which causes a real schism in programming styles because a great deal of C++ code must invariably call down into underlying C libraries.
- Don't use RTTI.
- Don't use C++ runtime wrapper for C runtime includes (
<cstdio>
,<cmath>
, etc.), use C runtime instead (<stdio.h>
,<math.h>
, etc.) - Don't use stream (
<iostream>
,<stringstream>
, etc.), use printf style functions instead. - Don't use anything from STL that allocates memory, unless you don't care about memory management. See CppCon 2015: Andrei Alexandrescu "std::allocator Is to Allocation what std::vector Is to Vexation" talk, and Why many AAA gamedev studios opt out of the STL thread for more info.
- Don't use metaprogramming excessively for academic masturbation. Use it in moderation, only where necessary, and where it reduces code complexity.
- Wary of any features introduced in current standard C++, ideally wait for improvements of those feature in next iteration of standard. Example
constexpr
from C++11 became usable in C++14 (per Jason Turner cppbestpractices.com curator)
Due to lag of adoption of C++ standard by compilers, OS distributions, etc. it's usually not possible to start using new useful language features immediately. General guideline is: if current year is C++year+5 then it's safe to start selectively using C++year's features. For example, if standard is C++11, and current year >= 2016 then it's probably safe. If standard required to compile your code is C++17 and year is 2016 then obviously you're practicing "Resume Driven Development" methodology. If you're doing this for open source project, then you're not creating something others can use.
UPDATE As of January 14th 2022, Orthodox C++ committee approved use of C++17.
- Embedded C++
- Nominal C++
- Sane C++
- Why Your C++ Should Be Simple
- C++, it’s not you. It’s me.
- "Keep It C-mple" Alexander Radchenko Sydney C++ Meetup
- A dialect of C++
- The Defold engine code style
- Any C source that compiles with C++ compiler.
- DOOM 3 BFG
- Qt (when built with no-rtti, no-exceptions)
- dear imgui
- bgfx
- TheForge
- Oryol
- Network Next SDK
Not that I doubt it is easy to outperform
std::vector::push_back
, but I doubt the real world performance impact ofstd::vector
is as brutal as you make it out to be, unless you have such numbers.By the way, how do you know that your benchmark actually performs the
push_back
s for all the vectors properly? I do not trust the results for this reason, even if it seems to be doing some amount of what you asked according to your numbers. I quickly checked thestd::vector
variant and it isn't a problem there, but I don't know about your vector implementation.I'm not trying to trash on your implementation, by the way, it is actually pretty interesting to me.
Also, TIL about clang's
trivial_abi
, neat.I do find it amusing that you defeated the compiler optimization in the benchmark merely by looping the
push_back
s until it couldn't unroll the loop and figure it out. I feel like the compiler ought to be smart enough to figure out there is no side effect to any of this, but it probably can't see past the complexity, especially because of the relocation logic.Which... honestly just makes me think
push_back
should be avoided in general, because it's going to have garbage codegen and optimization implications anyway, at least when you can get away usingresize
ahead of time and indexing, which, according to my experience with real-world code, is usually feasible. That usecase is probably significantly less affected by the code bloat and EH related issues you mention.It could very well be that the unchecked variant you mention benefits codegen a lot on that front. However, the preconditions you need to check for in your code to ensure it is safe seem in practice close enough to what you'd need to
.resize()
and index, though?What I do wish is that
std::vector
allowed resizing without initializing theT
s. With non-trivial types, I found that the compiler would sometimes still emit the memory zeroing code even when unnecessary. I worked around that by wrapping myT
in a ugly way, but it was trash.