Instantly share code, notes, and snippets.

Embed
What would you like to do?
Orthodox C++

Orthodox C++

What is Orthodox C++?

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.

Why not Modern C++?

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).

Why use Orthodox C++?

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.

Hello World in Orthodox C++

#include <stdio.h>

int main()
{
    printf("hello, world\n");
    return 0;
}

What should I use?

  • 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.
  • 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.
  • 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)

Is it safe to use any of Modern C++ features yet?

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.

Any other similar ideas?

Code examples

@dariomanesku

This comment has been minimized.

Show comment
Hide comment
@dariomanesku

dariomanesku commented Jan 16, 2016

πŸ‘

@ghost

This comment has been minimized.

Show comment
Hide comment
@ghost

ghost Jan 16, 2016

Yes please.

ghost commented Jan 16, 2016

Yes please.

@oblitum

This comment has been minimized.

Show comment
Hide comment
@oblitum

oblitum Jan 16, 2016

I think you should be more clear regarding "anything that allocates memory". What about "anything that allocates dynamic memory", or memory from the heap, or from free storage. If that is what you mean, maybe you're saying to not use std::array too, regarding memory I mean.

oblitum commented Jan 16, 2016

I think you should be more clear regarding "anything that allocates memory". What about "anything that allocates dynamic memory", or memory from the heap, or from free storage. If that is what you mean, maybe you're saying to not use std::array too, regarding memory I mean.

@bkaradzic

This comment has been minimized.

Show comment
Hide comment
@bkaradzic

bkaradzic Jan 16, 2016

@oblitum Memory from heap, stack is fine. But it's general because a lot of things allocate memory in STL.

Owner

bkaradzic commented Jan 16, 2016

@oblitum Memory from heap, stack is fine. But it's general because a lot of things allocate memory in STL.

@elnormous

This comment has been minimized.

Show comment
Hide comment
@elnormous

elnormous commented Jan 17, 2016

Fixed a typo in the word "adoption" here: https://gist.github.com/elvman/c5b5f081857089ccf695

@bkaradzic

This comment has been minimized.

Show comment
Hide comment
@bkaradzic
Owner

bkaradzic commented Jan 17, 2016

@elvman πŸ‘

@rgthomas

This comment has been minimized.

Show comment
Hide comment
@rgthomas

rgthomas Jan 17, 2016

Great name for this subset! One question: what are the drawbacks to using the C++ runtime wrappers like cstdio and cstdlib?

rgthomas commented Jan 17, 2016

Great name for this subset! One question: what are the drawbacks to using the C++ runtime wrappers like cstdio and cstdlib?

@bullno1

This comment has been minimized.

Show comment
Hide comment
@bullno1

bullno1 commented Jan 17, 2016

πŸ‘

@bkaradzic

This comment has been minimized.

Show comment
Hide comment
@bkaradzic

bkaradzic Jan 17, 2016

@rgthomas It's just a wrapper, it doesn't do anything useful. Just makes C headers pretend to be C++.

Owner

bkaradzic commented Jan 17, 2016

@rgthomas It's just a wrapper, it doesn't do anything useful. Just makes C headers pretend to be C++.

@quicknir

This comment has been minimized.

Show comment
Hide comment
@quicknir

quicknir Jan 17, 2016

Telling people not to use std::vector as general advice (as opposed to on some specific platform, or for some specific performance need) may actually be the worst C++ advice I've seen. It's hard to even imagine how many memory leaks vector has prevented, and how many man hours it's saved.

Streams are not particularly modern C++. They might not even exist if variadics weren't so late to the ball game. Variadic print functions look just like printf, perform similarly, but are much safer: https://github.com/cppformat/cppformat.

quicknir commented Jan 17, 2016

Telling people not to use std::vector as general advice (as opposed to on some specific platform, or for some specific performance need) may actually be the worst C++ advice I've seen. It's hard to even imagine how many memory leaks vector has prevented, and how many man hours it's saved.

Streams are not particularly modern C++. They might not even exist if variadics weren't so late to the ball game. Variadic print functions look just like printf, perform similarly, but are much safer: https://github.com/cppformat/cppformat.

@jcelerier

This comment has been minimized.

Show comment
Hide comment
@jcelerier

jcelerier Jan 17, 2016

features we used proved to be bad (like RTTI, exceptions, and streams),

source ?

jcelerier commented Jan 17, 2016

features we used proved to be bad (like RTTI, exceptions, and streams),

source ?

@Kos

This comment has been minimized.

Show comment
Hide comment
@Kos

Kos Jan 17, 2016

Agreeing on a particular representation of "string", "dynamic array" and "associative array" is one of the things I consider to pre-conditions for a mature language.

Kos commented Jan 17, 2016

Agreeing on a particular representation of "string", "dynamic array" and "associative array" is one of the things I consider to pre-conditions for a mature language.

@sasmaster

This comment has been minimized.

Show comment
Hide comment
@sasmaster

sasmaster Jan 17, 2016

Why RTTI is bad?

sasmaster commented Jan 17, 2016

Why RTTI is bad?

@Cygon

This comment has been minimized.

Show comment
Hide comment
@Cygon

Cygon Jan 17, 2016

I respectfully disagree with the extremes to which this idea is taken.

RTTI can be abused (not nearly as much as Java/.NET reflection, though), but it can also be a very neat and tidy puzzle piece that improves performance (see: optionally implemented interfaces!). Exceptions (combined with RAII) are the most efficient and clean error handling method programming languages have come up with so far. STL is useful and well thought out, but okay, even with custom allocators it can be challenging to manage micro-allocations and heap fragmentation on weaker platforms, so perhaps in a few cases roll-your-own is okay.

Source: 20 years of professional C++ development full time (while also trying to keep code maintainable by inexperienced developers)

Cygon commented Jan 17, 2016

I respectfully disagree with the extremes to which this idea is taken.

RTTI can be abused (not nearly as much as Java/.NET reflection, though), but it can also be a very neat and tidy puzzle piece that improves performance (see: optionally implemented interfaces!). Exceptions (combined with RAII) are the most efficient and clean error handling method programming languages have come up with so far. STL is useful and well thought out, but okay, even with custom allocators it can be challenging to manage micro-allocations and heap fragmentation on weaker platforms, so perhaps in a few cases roll-your-own is okay.

Source: 20 years of professional C++ development full time (while also trying to keep code maintainable by inexperienced developers)

@miguelmartin75

This comment has been minimized.

Show comment
Hide comment
@miguelmartin75

miguelmartin75 Jan 17, 2016

Just some thoughts.

Don't use anything from STL that allocates memory, unless you don't care about memory management.

What do I use instead? Do I have to make my own container(s)? Why shouldn't I use them? If you're going to say "don't use the STL" at least provide some reasoning and a viable alternative.

If I do roll my own containers, does that mean when I'm going to be using open source libraries there's going to be unnecessary duplicate code just for generic containers because they're not using the STL as you suggested?

Don't use C++ runtime wrapper for C runtime includes (, , etc.), use C runtime instead (<stdio.h>, <math.h>, etc.)

Aren't xxxx.h headers not actually apart of the C++ standard? I thought these were depreciated header files. Maybe I'm wrong, but cppreference doesn't seem to have these documented (only the cxxx.h variants).

Don't use exceptions.

I can understand this from a personal preference, and maybe performance issue if working on embedded devices. But imo, in some situations exceptions could be handy. Though I rarely use them myself.

Don't use RTTI.

Okay, I agree with this. Since there's no standard way for a compiler to implement them, and they are usually a sign of bad design. And if you really do need it, you can easily do it with integers identifying which type it is.

Don't use stream (, , etc.), use printf style functions instead.

Why? I don't see a reason why not to. Other than maybe a "performance reason", but I'm fairly sure printf and streams have similar performance. If anything printf is defiantly less safe, unless you make yourself a safer version using templates.

miguelmartin75 commented Jan 17, 2016

Just some thoughts.

Don't use anything from STL that allocates memory, unless you don't care about memory management.

What do I use instead? Do I have to make my own container(s)? Why shouldn't I use them? If you're going to say "don't use the STL" at least provide some reasoning and a viable alternative.

If I do roll my own containers, does that mean when I'm going to be using open source libraries there's going to be unnecessary duplicate code just for generic containers because they're not using the STL as you suggested?

Don't use C++ runtime wrapper for C runtime includes (, , etc.), use C runtime instead (<stdio.h>, <math.h>, etc.)

Aren't xxxx.h headers not actually apart of the C++ standard? I thought these were depreciated header files. Maybe I'm wrong, but cppreference doesn't seem to have these documented (only the cxxx.h variants).

Don't use exceptions.

I can understand this from a personal preference, and maybe performance issue if working on embedded devices. But imo, in some situations exceptions could be handy. Though I rarely use them myself.

Don't use RTTI.

Okay, I agree with this. Since there's no standard way for a compiler to implement them, and they are usually a sign of bad design. And if you really do need it, you can easily do it with integers identifying which type it is.

Don't use stream (, , etc.), use printf style functions instead.

Why? I don't see a reason why not to. Other than maybe a "performance reason", but I'm fairly sure printf and streams have similar performance. If anything printf is defiantly less safe, unless you make yourself a safer version using templates.

@bkaradzic

This comment has been minimized.

Show comment
Hide comment
@bkaradzic

bkaradzic Jan 17, 2016

@sasmaster It's string based. When you use it, it bloats executable with strings of all classes, and it's slow because does string comparisons.

Owner

bkaradzic commented Jan 17, 2016

@sasmaster It's string based. When you use it, it bloats executable with strings of all classes, and it's slow because does string comparisons.

@bkaradzic

This comment has been minimized.

Show comment
Hide comment
@bkaradzic

bkaradzic Jan 17, 2016

@jcelerier Source is personal experience.

Owner

bkaradzic commented Jan 17, 2016

@jcelerier Source is personal experience.

@bkaradzic

This comment has been minimized.

Show comment
Hide comment
@bkaradzic

bkaradzic Jan 17, 2016

@miguelmartin75

But imo, in some situations exceptions could be handy. Though I rarely use them myself.

Exceptions are not some magic that you enable and your code is just exception safe. Style of code, support to be exception safe, and extra complexity is significant. And not worth for "rarely" used cases.

Owner

bkaradzic commented Jan 17, 2016

@miguelmartin75

But imo, in some situations exceptions could be handy. Though I rarely use them myself.

Exceptions are not some magic that you enable and your code is just exception safe. Style of code, support to be exception safe, and extra complexity is significant. And not worth for "rarely" used cases.

@quicknir

This comment has been minimized.

Show comment
Hide comment
@quicknir

quicknir Jan 17, 2016

@bkaradzic The weak exception guarantee is extremely easy to get. You just don't leave unowned resources lying around. Unowned resources for more than a few lines are bad news even without exceptions, it may be fine at the time you commit it but if someone else modifies the code they could easily add an early return that causes a memory leak. Code should be as locally correct as possible; this means immediately taking ownership of resources, and this leads to the weak exception guarantee (which is good enough for many applications).

RTTI isn't super fast, but there are plenty of situations where the performance doesn't matter enough for that to be important. I agree that it's not the first tool you should turn to, but you do occasionally run into things that are awkward to do any other way, where you would likely roll your own version of RTTI if you didn't use regular RTTI. This can be ok too if you can justify the man hours for e.g. performance gain, but often just using RTTI will be easiest and least bug prone.

quicknir commented Jan 17, 2016

@bkaradzic The weak exception guarantee is extremely easy to get. You just don't leave unowned resources lying around. Unowned resources for more than a few lines are bad news even without exceptions, it may be fine at the time you commit it but if someone else modifies the code they could easily add an early return that causes a memory leak. Code should be as locally correct as possible; this means immediately taking ownership of resources, and this leads to the weak exception guarantee (which is good enough for many applications).

RTTI isn't super fast, but there are plenty of situations where the performance doesn't matter enough for that to be important. I agree that it's not the first tool you should turn to, but you do occasionally run into things that are awkward to do any other way, where you would likely roll your own version of RTTI if you didn't use regular RTTI. This can be ok too if you can justify the man hours for e.g. performance gain, but often just using RTTI will be easiest and least bug prone.

@OndraVoves

This comment has been minimized.

Show comment
Hide comment
@OndraVoves

OndraVoves commented Jan 18, 2016

πŸ‘

@vareille

This comment has been minimized.

Show comment
Hide comment
@vareille

vareille Jan 18, 2016

Your hello world doesn't show any C++ feature,
So it doesn't show how this is different from pure C.

what is your standing on references ?

vareille commented Jan 18, 2016

Your hello world doesn't show any C++ feature,
So it doesn't show how this is different from pure C.

what is your standing on references ?

@bkaradzic

This comment has been minimized.

Show comment
Hide comment
@bkaradzic

bkaradzic Jan 18, 2016

@ chafporte

Your hello world doesn't show any C++ feature,
So it doesn't show how this is different from pure C.

Exactly, that's the point. If you don't need anything from C++ then don't use it, just because you want to be different from C.

what is your standing on references ?

They are fine.

Owner

bkaradzic commented Jan 18, 2016

@ chafporte

Your hello world doesn't show any C++ feature,
So it doesn't show how this is different from pure C.

Exactly, that's the point. If you don't need anything from C++ then don't use it, just because you want to be different from C.

what is your standing on references ?

They are fine.

@dariomanesku

This comment has been minimized.

Show comment
Hide comment
@dariomanesku

dariomanesku Jan 18, 2016

what is your standing on references?

Pointless cruft. They're now useful for making C++ code fit nicely together like a puzzle (different constructors, overloading, operators, templates... etc.) but it would've been better if they didn't got introduced in the language at all.

dariomanesku commented Jan 18, 2016

what is your standing on references?

Pointless cruft. They're now useful for making C++ code fit nicely together like a puzzle (different constructors, overloading, operators, templates... etc.) but it would've been better if they didn't got introduced in the language at all.

@Forceflow

This comment has been minimized.

Show comment
Hide comment
@Forceflow

Forceflow Jan 18, 2016

Amen, brother! πŸ‘ :

Forceflow commented Jan 18, 2016

Amen, brother! πŸ‘ :

@mosra

This comment has been minimized.

Show comment
Hide comment
@mosra

mosra Jan 18, 2016

Good points, although I have to disagree with this one:

Don't use C++ runtime wrapper for C runtime includes (<cstdio>, <cmath>, etc.), use C runtime instead (<stdio.h>, <math.h>, etc.)

Why would I do that? The C++ includes are not just wrappers, they are converting macro-ridden C insanities into proper inline functions and making them overloaded so you don't have to backtrack your types to use proper abs(), fabs(), llabs() or whatnot. That's in my opinion one of the best features that C++ offers on top of C library. And besides that, I really like the idea of the standard library tools prefixed with std:: to distinguish them from other "non-standard-library" code.

  • Qt

What? Qt? You say that I should not use STL containers because STL containers allocate. That's right, but it is possible to get around many of the allocations with some effort. Qt, on the other hand, allocates all the time and you have absolutely no control about that. I had the "pleasure" of using Qt as a backbone for touchscreen UI running 60 FPS on embedded devices and it's impossible to do anything in it without allocating on every second line. Because of their design decisions like D-pointers and UTF-16, even the tiniest empty objects are allocating and you can't even create a mutex or append a integer to pre-reserved string without spotting tons of mallocs in your profiler. Not to mention the crazy MOC and buildsystem hijacking. Qt is really not something I would put here as an example of well-designed library.

mosra commented Jan 18, 2016

Good points, although I have to disagree with this one:

Don't use C++ runtime wrapper for C runtime includes (<cstdio>, <cmath>, etc.), use C runtime instead (<stdio.h>, <math.h>, etc.)

Why would I do that? The C++ includes are not just wrappers, they are converting macro-ridden C insanities into proper inline functions and making them overloaded so you don't have to backtrack your types to use proper abs(), fabs(), llabs() or whatnot. That's in my opinion one of the best features that C++ offers on top of C library. And besides that, I really like the idea of the standard library tools prefixed with std:: to distinguish them from other "non-standard-library" code.

  • Qt

What? Qt? You say that I should not use STL containers because STL containers allocate. That's right, but it is possible to get around many of the allocations with some effort. Qt, on the other hand, allocates all the time and you have absolutely no control about that. I had the "pleasure" of using Qt as a backbone for touchscreen UI running 60 FPS on embedded devices and it's impossible to do anything in it without allocating on every second line. Because of their design decisions like D-pointers and UTF-16, even the tiniest empty objects are allocating and you can't even create a mutex or append a integer to pre-reserved string without spotting tons of mallocs in your profiler. Not to mention the crazy MOC and buildsystem hijacking. Qt is really not something I would put here as an example of well-designed library.

@EddyLuten

This comment has been minimized.

Show comment
Hide comment
@EddyLuten

EddyLuten commented Jan 18, 2016

πŸ‘

@bkaradzic

This comment has been minimized.

Show comment
Hide comment
@bkaradzic

bkaradzic Jan 19, 2016

@mosra

What? Qt? You say that I should not use STL containers because STL containers allocate.

No, I said if you don't care about precise memory management. Qt is usually used for utilities so memory requirements are a little bit relaxed there.

Qt is really not something I would put here as an example of well-designed library.

It's not example of "well-designed", just example of Orthodox C++ code. I personally think Qt is well designed, the most sane UI framework I used (I used MFC, ATL, wxWidgets, etc.)

Owner

bkaradzic commented Jan 19, 2016

@mosra

What? Qt? You say that I should not use STL containers because STL containers allocate.

No, I said if you don't care about precise memory management. Qt is usually used for utilities so memory requirements are a little bit relaxed there.

Qt is really not something I would put here as an example of well-designed library.

It's not example of "well-designed", just example of Orthodox C++ code. I personally think Qt is well designed, the most sane UI framework I used (I used MFC, ATL, wxWidgets, etc.)

@kariem2k

This comment has been minimized.

Show comment
Hide comment
@kariem2k

kariem2k Mar 11, 2016

Maybe some people can write clang extension to only allow this subset of C++.

kariem2k commented Mar 11, 2016

Maybe some people can write clang extension to only allow this subset of C++.

@kterhorst

This comment has been minimized.

Show comment
Hide comment
@kterhorst

kterhorst Mar 15, 2016

My main argument against using STL is the insane amount of code bloat that it generates and its associated increase in compilation time due to excessive use of templates. For example, a simple program that spawns some dummy threads and inserts them into an unordered_set yields an almost 1MB .obj file (VC++2015, Win64, Debug build). In contrast, an object file generated from a 300kb (more than 12000 LOC) C++ source file from RBDOOM3 only has a size of 920KB. Using STL is fine for small applications and tools but when it comes to bigger projects and open source libraries I am against STL, extensive template usage and everything else that bloats the code.

kterhorst commented Mar 15, 2016

My main argument against using STL is the insane amount of code bloat that it generates and its associated increase in compilation time due to excessive use of templates. For example, a simple program that spawns some dummy threads and inserts them into an unordered_set yields an almost 1MB .obj file (VC++2015, Win64, Debug build). In contrast, an object file generated from a 300kb (more than 12000 LOC) C++ source file from RBDOOM3 only has a size of 920KB. Using STL is fine for small applications and tools but when it comes to bigger projects and open source libraries I am against STL, extensive template usage and everything else that bloats the code.

@sabotage3d

This comment has been minimized.

Show comment
Hide comment
@sabotage3d

sabotage3d Sep 2, 2016

In this Orthodox C++ paradigm can we still use inheritance and virtual functions?

sabotage3d commented Sep 2, 2016

In this Orthodox C++ paradigm can we still use inheritance and virtual functions?

@DrCroco

This comment has been minimized.

Show comment
Hide comment
@DrCroco

DrCroco Oct 7, 2016

I'd say that you're not orthodox enough. First of all, there's no real use for STL. Complicated data structures are never generic, they must be carefully designed for each particular task. From the other hand, primitive data structures, such as vector or list, give almost no profit, but are way harder to debug/maintain than hand-crafted linked lists and resizeable arrays. Furthermore, the very idea of a container class is weird. Container is a thing that contains my data, but a class is a thing I can't (well, must not) look into. So, container classes is when I don't see my data. Hell, I don't want to be blind at the debugging time, as debugging is hard enough without it.

Next, there's no such thing as 'safe features from new standards'. All technical standards since around 1995 or so are simply terrorist acts, and these committees that prepare and create new standards are dangerous international terrorist organizations, and in particular, ISO with their policy of keeping standards secret and selling their texts for money, is the most dangerous terrorist organization in the world, way more dangerous that Al Qaeda. ALL they do is bad. So everything they do must be banned right off, without consideration.

I'm nevertheless glad to see that someone in the world sees the things almost as they are. Modern software industry looks like being made of complete idiots, and there must be some way out of this situation.

DrCroco commented Oct 7, 2016

I'd say that you're not orthodox enough. First of all, there's no real use for STL. Complicated data structures are never generic, they must be carefully designed for each particular task. From the other hand, primitive data structures, such as vector or list, give almost no profit, but are way harder to debug/maintain than hand-crafted linked lists and resizeable arrays. Furthermore, the very idea of a container class is weird. Container is a thing that contains my data, but a class is a thing I can't (well, must not) look into. So, container classes is when I don't see my data. Hell, I don't want to be blind at the debugging time, as debugging is hard enough without it.

Next, there's no such thing as 'safe features from new standards'. All technical standards since around 1995 or so are simply terrorist acts, and these committees that prepare and create new standards are dangerous international terrorist organizations, and in particular, ISO with their policy of keeping standards secret and selling their texts for money, is the most dangerous terrorist organization in the world, way more dangerous that Al Qaeda. ALL they do is bad. So everything they do must be banned right off, without consideration.

I'm nevertheless glad to see that someone in the world sees the things almost as they are. Modern software industry looks like being made of complete idiots, and there must be some way out of this situation.

@johnnovak

This comment has been minimized.

Show comment
Hide comment
@johnnovak

johnnovak Oct 20, 2016

Well said! +1000

johnnovak commented Oct 20, 2016

Well said! +1000

@FrankHB

This comment has been minimized.

Show comment
Hide comment
@FrankHB

FrankHB Jan 11, 2017

It is suspicious to title things with high density of obvious inconsistency with "orthodox".

For example, the following entries are self-contradict:

Don't use C++ runtime wrapper for C runtime includes (, , etc.), use C runtime instead (<stdio.h>, <math.h>, etc.)
Don't use stream (, , etc.), use printf style functions instead.

How to avoid "stream"? In fact, <stdio.h> provides exactly the APIs for streams. One of them is called the standard (text) output stream, i.e. stdout. And printf relies on it implicitly.

And seems you were allowing <cstddef> (which is hardly a kind of "runtime wrapper") ... huh, is this you want?

And... you forget to rule out <streambuf>, etc...

Some are confusing, like:

Don't use anything from STL that allocates memory, unless you don't care about memory management.

About what? STL was never existed in "modern C++". (You were not meaning Stephan T. Lavavej, right?)

FrankHB commented Jan 11, 2017

It is suspicious to title things with high density of obvious inconsistency with "orthodox".

For example, the following entries are self-contradict:

Don't use C++ runtime wrapper for C runtime includes (, , etc.), use C runtime instead (<stdio.h>, <math.h>, etc.)
Don't use stream (, , etc.), use printf style functions instead.

How to avoid "stream"? In fact, <stdio.h> provides exactly the APIs for streams. One of them is called the standard (text) output stream, i.e. stdout. And printf relies on it implicitly.

And seems you were allowing <cstddef> (which is hardly a kind of "runtime wrapper") ... huh, is this you want?

And... you forget to rule out <streambuf>, etc...

Some are confusing, like:

Don't use anything from STL that allocates memory, unless you don't care about memory management.

About what? STL was never existed in "modern C++". (You were not meaning Stephan T. Lavavej, right?)

@FrankHB

This comment has been minimized.

Show comment
Hide comment
@FrankHB

FrankHB Jan 11, 2017

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.

I am glad to highly praise such ideas, except you youths are taking it too simple - if you really, really want to "selectively" use the language... And "probably" is hardly enough for tasks in reality once you meet nasty implementation regressions.

Seriously, have you ever done such audit in practice? ... e.g. as the basis, by producing something like this.

FrankHB commented Jan 11, 2017

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.

I am glad to highly praise such ideas, except you youths are taking it too simple - if you really, really want to "selectively" use the language... And "probably" is hardly enough for tasks in reality once you meet nasty implementation regressions.

Seriously, have you ever done such audit in practice? ... e.g. as the basis, by producing something like this.

@FrankHB

This comment has been minimized.

Show comment
Hide comment
@FrankHB

FrankHB Jan 11, 2017

It's not example of "well-designed", just example of Orthodox C++ code. I personally think Qt is well designed, the most sane UI framework I used (I used MFC, ATL, wxWidgets, etc.)

That's false. Typical "orthodox" Qt-based source is even never a well-formed example for any version of C++. Tools like moc have to be pipelined before feeding the source to any sane C++ implementation, otherwise you'll fail to build. As of non-"orthodox" ones... how do you expect any C++ implementation to be able to translate QML or QSS code successfully?

FrankHB commented Jan 11, 2017

It's not example of "well-designed", just example of Orthodox C++ code. I personally think Qt is well designed, the most sane UI framework I used (I used MFC, ATL, wxWidgets, etc.)

That's false. Typical "orthodox" Qt-based source is even never a well-formed example for any version of C++. Tools like moc have to be pipelined before feeding the source to any sane C++ implementation, otherwise you'll fail to build. As of non-"orthodox" ones... how do you expect any C++ implementation to be able to translate QML or QSS code successfully?

@10se1ucgo

This comment has been minimized.

Show comment
Hide comment
@10se1ucgo

10se1ucgo Jan 31, 2017

there's a special kind of hell for you guys

10se1ucgo commented Jan 31, 2017

there's a special kind of hell for you guys

@ennmichael

This comment has been minimized.

Show comment
Hide comment
@ennmichael

ennmichael commented Feb 15, 2017

kys

@RantyDave

This comment has been minimized.

Show comment
Hide comment
@RantyDave

RantyDave Feb 28, 2017

Oh man, I had to use Boost Geometry. I'd been away from C++ for a few years and it make me think my brain had dissolved. I got it to do what it's supposed to - and it did it very well. But, damn, did that hurt.

RantyDave commented Feb 28, 2017

Oh man, I had to use Boost Geometry. I'd been away from C++ for a few years and it make me think my brain had dissolved. I got it to do what it's supposed to - and it did it very well. But, damn, did that hurt.

@ahoka

This comment has been minimized.

Show comment
Hide comment
@ahoka

ahoka Feb 28, 2017

Don't tell me what to use.

ahoka commented Feb 28, 2017

Don't tell me what to use.

@PhilCK

This comment has been minimized.

Show comment
Hide comment
@PhilCK

PhilCK Jun 3, 2017

πŸ‘

PhilCK commented Jun 3, 2017

πŸ‘

@njlr

This comment has been minimized.

Show comment
Hide comment
@njlr

njlr Jun 22, 2017

Hmm... I'm not sure I agree entirely.

Some C++ features that make code more readable and lead to fewer bugs:

  • RAII
  • For-each loops (C++ 11)
  • Smart pointers
  • Templated containers
  • auto

+1 for keeping things simple though.

njlr commented Jun 22, 2017

Hmm... I'm not sure I agree entirely.

Some C++ features that make code more readable and lead to fewer bugs:

  • RAII
  • For-each loops (C++ 11)
  • Smart pointers
  • Templated containers
  • auto

+1 for keeping things simple though.

@metacritical

This comment has been minimized.

Show comment
Hide comment
@metacritical

metacritical Jun 22, 2017

Words of wisdom! Atleast some in the C++ community realise that, a large number of CPP features are unnecessary or garbage at times. Leaning back to C is ideal in my opinion or in this case orthodox C++ πŸ‘

metacritical commented Jun 22, 2017

Words of wisdom! Atleast some in the C++ community realise that, a large number of CPP features are unnecessary or garbage at times. Leaning back to C is ideal in my opinion or in this case orthodox C++ πŸ‘

@nicebyte

This comment has been minimized.

Show comment
Hide comment
@nicebyte

nicebyte Jun 27, 2017

I mostly don't like this, because the text never mentions any of the motivation behind the advice.

C-like C++ is good start

What is "C-like"? This needs to be specified. Manually managing memory with malloc and free is "C-like", yet no C++ code should be doing that. Writing structs with callback fields is also very C-like, yet it's just copying the functionality provided by virtual methods.

Don't do this, the end of "design rationale" in Orthodox C++ should be immedately after "Quite simple, and it is usable. EOF".

Yeah I don't like boost either

Don't use exceptions.

Don't see the point in this. I guess one could make all your functions return an error state and use output parameters everywhere, I don't see the appeal of that, unless you have to interface with code that is exception-unaware. Also, I need to see some proof that exceptions meaningfully affect performance in the non-exceptional scenario.

Don't use RTTI.

Sure, except the cases when you actually need it are so rare that it almost doesn't matter.

Don't use C++ runtime wrapper for C runtime includes (, , etc.), use C runtime instead (<stdio.h>, <math.h>, etc.)

Why?

Don't use stream (, , etc.), use printf style functions instead.

Again, no justification for this. It could be mentioned at least that printf-style is more amenable to i18n.
But then again

std::cerr << my3dvector << "\n";

is better than

fprintf(stderr, "%f %f %f\n", my3dvector[0], my3dvector[1], my3dvector[2]);

Don't use anything from STL that allocates memory, unless you don't care about memory management.

...which is actually most of the time. Instead of blindly following this advice and reinventing the wheel seventeen times a day, understand your use case and make a judgement what would be the best. Yes, if you need your hash table to always occupy a small fixed amount of memory, don't use unordered_map, roll your own implementation with open addressing or something.

Don't use metaprogramming excessively for academic masturbation. Use it in moderation, only where necessary, and where it reduces code complexity.

Most of this stuff isn't even for application developers anyway. People who will most benefit from these features are library authors (i.e. STL). So yeah, I agree with the sentiment.

nicebyte commented Jun 27, 2017

I mostly don't like this, because the text never mentions any of the motivation behind the advice.

C-like C++ is good start

What is "C-like"? This needs to be specified. Manually managing memory with malloc and free is "C-like", yet no C++ code should be doing that. Writing structs with callback fields is also very C-like, yet it's just copying the functionality provided by virtual methods.

Don't do this, the end of "design rationale" in Orthodox C++ should be immedately after "Quite simple, and it is usable. EOF".

Yeah I don't like boost either

Don't use exceptions.

Don't see the point in this. I guess one could make all your functions return an error state and use output parameters everywhere, I don't see the appeal of that, unless you have to interface with code that is exception-unaware. Also, I need to see some proof that exceptions meaningfully affect performance in the non-exceptional scenario.

Don't use RTTI.

Sure, except the cases when you actually need it are so rare that it almost doesn't matter.

Don't use C++ runtime wrapper for C runtime includes (, , etc.), use C runtime instead (<stdio.h>, <math.h>, etc.)

Why?

Don't use stream (, , etc.), use printf style functions instead.

Again, no justification for this. It could be mentioned at least that printf-style is more amenable to i18n.
But then again

std::cerr << my3dvector << "\n";

is better than

fprintf(stderr, "%f %f %f\n", my3dvector[0], my3dvector[1], my3dvector[2]);

Don't use anything from STL that allocates memory, unless you don't care about memory management.

...which is actually most of the time. Instead of blindly following this advice and reinventing the wheel seventeen times a day, understand your use case and make a judgement what would be the best. Yes, if you need your hash table to always occupy a small fixed amount of memory, don't use unordered_map, roll your own implementation with open addressing or something.

Don't use metaprogramming excessively for academic masturbation. Use it in moderation, only where necessary, and where it reduces code complexity.

Most of this stuff isn't even for application developers anyway. People who will most benefit from these features are library authors (i.e. STL). So yeah, I agree with the sentiment.

@keebus

This comment has been minimized.

Show comment
Hide comment
@keebus

keebus Jun 29, 2017

@nicebyte I disagree. If you're writing low-level data oriented code, virtual classes almost always get in your way. After years of practical low-level professional game engine programming and personal computer science research, I dare to say when you write code like a game engine you end up in a situations where you either have lots of objects that must be updated the frequently and efficiently or you are dealing with large and few entities (e.g. anything called *Manager, *System, etc). In the latter case an opaque struct pointer followed by several API functions that operate on an instance of that (opaque) struct is expressive enough to create a neat abstraction and I'd say much more OOP than what you'd get in C++, as all of its private implementation details (in the .c) are truly hidden from its interface (in the .h) where in C++ you are forced to declare its private members in the class interface (yuck #1). The former, you want to process all those objects in batches, grouping all the operations and common data relevant to a single object type in a "Type" struct. Something you simply cannot do with C++ because virtual tables are hidden and not supposed to be fetched or populated manually (yuck #2).

Want a practical example? Consider writing a raytracer. You could go ahead and make a Shape virtual interface that has an Intersect() method. Then you have a AggregateShape that has a std::vector<Shape*> (yuck #3) and its own Intersect() method that simply calls each child Shape Intersect in turn and returns the closest to the ray origin. Sure this simple enough, but I'd argue not good enough. A better way is, for instance, having a ShapeType struct with a uint32 containing a unique id and a bunch of function pointers such as (* Intersect)(). your AggregateShape can now be implemented with an array of ShapeTypes (using their unique ID as index) and its Intersect() function being arguably considerably faster, as it can be implemented per ShapeType and not per (any) Shape. New iteration, new ShapeType, store the address of its Intersect function in a register, call it in a hot loop for each shape of that type (naturally stored in an array by value, and not by pointer like in the previous case). Oh, and this also uses less memory, since you have removed the vptr from each Shape object.

No matter how hard I try, the more experienced I become, the less attractive C++ gets. Not to mention the various serious design mistakes the language has I will skip because I need to work. Let's just say C++ is a language that is prone to complexity which exponentially leads to even more complexity and bugs.

keebus commented Jun 29, 2017

@nicebyte I disagree. If you're writing low-level data oriented code, virtual classes almost always get in your way. After years of practical low-level professional game engine programming and personal computer science research, I dare to say when you write code like a game engine you end up in a situations where you either have lots of objects that must be updated the frequently and efficiently or you are dealing with large and few entities (e.g. anything called *Manager, *System, etc). In the latter case an opaque struct pointer followed by several API functions that operate on an instance of that (opaque) struct is expressive enough to create a neat abstraction and I'd say much more OOP than what you'd get in C++, as all of its private implementation details (in the .c) are truly hidden from its interface (in the .h) where in C++ you are forced to declare its private members in the class interface (yuck #1). The former, you want to process all those objects in batches, grouping all the operations and common data relevant to a single object type in a "Type" struct. Something you simply cannot do with C++ because virtual tables are hidden and not supposed to be fetched or populated manually (yuck #2).

Want a practical example? Consider writing a raytracer. You could go ahead and make a Shape virtual interface that has an Intersect() method. Then you have a AggregateShape that has a std::vector<Shape*> (yuck #3) and its own Intersect() method that simply calls each child Shape Intersect in turn and returns the closest to the ray origin. Sure this simple enough, but I'd argue not good enough. A better way is, for instance, having a ShapeType struct with a uint32 containing a unique id and a bunch of function pointers such as (* Intersect)(). your AggregateShape can now be implemented with an array of ShapeTypes (using their unique ID as index) and its Intersect() function being arguably considerably faster, as it can be implemented per ShapeType and not per (any) Shape. New iteration, new ShapeType, store the address of its Intersect function in a register, call it in a hot loop for each shape of that type (naturally stored in an array by value, and not by pointer like in the previous case). Oh, and this also uses less memory, since you have removed the vptr from each Shape object.

No matter how hard I try, the more experienced I become, the less attractive C++ gets. Not to mention the various serious design mistakes the language has I will skip because I need to work. Let's just say C++ is a language that is prone to complexity which exponentially leads to even more complexity and bugs.

@keebus

This comment has been minimized.

Show comment
Hide comment
@keebus

keebus Jun 29, 2017

I understand and agree with OP's arguments, but I believe the "C++ is huge but you just need to use what you need" is fallacious. Every language has an idiomatic form. Yes you can write C-like C++, but that is not idiomatic C++. This is especially troublesome in a large company with many programmers. Coding standards are hard to enforce, and no matter how hard one tries, large codebases tend to have somewhat mixed styles. In other words, I understand and agree with the intentions of using only a subset of C++, but they're still there, and we're paying the cost for them (complex, slow, buggy compilers) even if we don't use them. It is a workaround not a a solution and should not stop us experiment and develop alternatives altogether.

keebus commented Jun 29, 2017

I understand and agree with OP's arguments, but I believe the "C++ is huge but you just need to use what you need" is fallacious. Every language has an idiomatic form. Yes you can write C-like C++, but that is not idiomatic C++. This is especially troublesome in a large company with many programmers. Coding standards are hard to enforce, and no matter how hard one tries, large codebases tend to have somewhat mixed styles. In other words, I understand and agree with the intentions of using only a subset of C++, but they're still there, and we're paying the cost for them (complex, slow, buggy compilers) even if we don't use them. It is a workaround not a a solution and should not stop us experiment and develop alternatives altogether.

@dud3

This comment has been minimized.

Show comment
Hide comment
@dud3

dud3 Jul 1, 2017

Sad can't star this gist more than once.

dud3 commented Jul 1, 2017

Sad can't star this gist more than once.

@nicebyte

This comment has been minimized.

Show comment
Hide comment
@nicebyte

nicebyte Jul 6, 2017

@keebus I want to address your reply.

First, I reject the premise that having to declare private members in the header is somehow defective. Knowing the size of the object within a TU has practical advantage: it allows for allocating it on the stack (or using some other form of fast allocation, i.e. from a pool of same-sized blocks) while opaque structures leave you with dynamic allocation as the only option (unless you explicitly make sure that they work with your fast allocator). I don't think the aesthetic argument of "it's cleaner that way" is compelling enough.

In your example, you're simply reinventing the mechanism of a virtual call. I could only see it being faster if you group your shapes by type in a single array (which would require sorting it), or have explicit "shape groups" tagged by corresponding shape types that have arrays of shapes only of that type, i.e.:

typedef struct {
/* whatever */
} shape;

typedef struct {
 shape_type type;
 shape *shapes;
 size_t num_shapes;
} shape_group;

typedef struct {
 shape_group *groups;
 size_t num_groups;
} aggregate_shape;

which would require carefully constructing the aggregate shape. Whether the possible speedup is worth the ugly interface is not obvious to me at all.

With all that being said, I could still live in a world with no virtual methods or even classes, where "t->x()" is just syntactic sugar for "x(t)".
But C takes away deterministic object lifetime and templates, replacing them with the pleasure of debugging resource leaks and macro shitsauce, and I don't like that.

To sum up my position on the matter, it is better to evolve from where C++ is, either by adding to the language (short-term) or making a new one (long-term), rather than regressing back into C.

nicebyte commented Jul 6, 2017

@keebus I want to address your reply.

First, I reject the premise that having to declare private members in the header is somehow defective. Knowing the size of the object within a TU has practical advantage: it allows for allocating it on the stack (or using some other form of fast allocation, i.e. from a pool of same-sized blocks) while opaque structures leave you with dynamic allocation as the only option (unless you explicitly make sure that they work with your fast allocator). I don't think the aesthetic argument of "it's cleaner that way" is compelling enough.

In your example, you're simply reinventing the mechanism of a virtual call. I could only see it being faster if you group your shapes by type in a single array (which would require sorting it), or have explicit "shape groups" tagged by corresponding shape types that have arrays of shapes only of that type, i.e.:

typedef struct {
/* whatever */
} shape;

typedef struct {
 shape_type type;
 shape *shapes;
 size_t num_shapes;
} shape_group;

typedef struct {
 shape_group *groups;
 size_t num_groups;
} aggregate_shape;

which would require carefully constructing the aggregate shape. Whether the possible speedup is worth the ugly interface is not obvious to me at all.

With all that being said, I could still live in a world with no virtual methods or even classes, where "t->x()" is just syntactic sugar for "x(t)".
But C takes away deterministic object lifetime and templates, replacing them with the pleasure of debugging resource leaks and macro shitsauce, and I don't like that.

To sum up my position on the matter, it is better to evolve from where C++ is, either by adding to the language (short-term) or making a new one (long-term), rather than regressing back into C.

@AdrienTD

This comment has been minimized.

Show comment
Hide comment
@AdrienTD

AdrienTD Aug 23, 2017

Exactly my style! πŸ‘

AdrienTD commented Aug 23, 2017

Exactly my style! πŸ‘

@alicemargatroid

This comment has been minimized.

Show comment
Hide comment
@alicemargatroid

alicemargatroid Nov 18, 2017

πŸ‘Ž C++14 was usable by GCC/Clang the moment it came out.

alicemargatroid commented Nov 18, 2017

πŸ‘Ž C++14 was usable by GCC/Clang the moment it came out.

@davegorst

This comment has been minimized.

Show comment
Hide comment
@davegorst

davegorst Dec 7, 2017

wxWidgets (http://wxwidgets.org/) could be said to be similar (at least it was the last time I looked at it), for reasons of portability.

davegorst commented Dec 7, 2017

wxWidgets (http://wxwidgets.org/) could be said to be similar (at least it was the last time I looked at it), for reasons of portability.

@Lectem

This comment has been minimized.

Show comment
Hide comment
@Lectem

Lectem Dec 16, 2017

C++14 was usable by GCC/Clang the moment it came out.

That's the exception though. C++17 will still take a while before being supported by all 3 major compilers, and (sadly) even more time to end up on developpers machines. That's not even talking about features that are already being amended because forced to ship.
I love C++ and most of what is being done by the committee, but the sad part is that a lot of the new features are just "metaprogramming excessively for academic masturbation.".

Lectem commented Dec 16, 2017

C++14 was usable by GCC/Clang the moment it came out.

That's the exception though. C++17 will still take a while before being supported by all 3 major compilers, and (sadly) even more time to end up on developpers machines. That's not even talking about features that are already being amended because forced to ship.
I love C++ and most of what is being done by the committee, but the sad part is that a lot of the new features are just "metaprogramming excessively for academic masturbation.".

@bkaradzic

This comment has been minimized.

Show comment
Hide comment
@bkaradzic

bkaradzic Dec 23, 2017

@alicemargatroid GCC/Clang are not only compilers used. Think Visual Studio. Also not all target platforms support newest compilers even if they are GCC/Clang or MSVC based.

Owner

bkaradzic commented Dec 23, 2017

@alicemargatroid GCC/Clang are not only compilers used. Think Visual Studio. Also not all target platforms support newest compilers even if they are GCC/Clang or MSVC based.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment