30 comments

[ 2.9 ms ] story [ 67.2 ms ] thread
In all my code, #include <vector> only appears once. That's because I have a file std_headers.h, which includes <stdlib>, <algorithm>, <string>, <vector>, and dozens more. With several #ifdefs for linux, FreeBSD, and Darwin and some embedded systems. Every other c++ file includes it. If you're curious: https://github.com/tlbtlbtlb/tlbcore/blob/master/common/std_...

I hate having a bunch of boilerplate at the top of each file.

Not a great idea if you care about compile times.
Unless they use pre compiled headers.
I've tried a few times to make PCH work reliably, but it's hard to get the Makefile right. My current project compiles both a regular executable and a Node.js module, which have different build processes.

Is there an OS project with a reliable and portable PCH build process? Searching github didn't find me anything to copy from.

pch is non portable at the moment.
I just went through the process of making a reliable and portable PCH build process, and it's a lot of code to get right, because clang, gcc, and cl.exe all have quite different models for how a pch should work.

cl.exe in particular has an insane set of requirements: - you can't compile the pch by itself, you have to have a cpp that #include's it and compile that; - then you have to link the resulting .obj file into the final binary; - if you use /Zi and you compile different versions of the pch for different headers, you have to have exactly one separate .pdb file for each compiled version of the pch; - if you have more, it will complain that some (pch, cpp) tuple do not share a .pdb; - if you have less, it will stochastically fail to open the global pdb file (because the pch writing process expects not to have contention for the pdb, even though it's using mspdbsrv and /FS for contention)

Very cool analysis.

I would add: A lot of regex work is still being done out of boost.regex; maybe people still haven't done the switch for most projects? For example, I think boost.regex is still used in Chrome.

Just ran your code on boost, looks like "shared_mutex" is the least used header there.

I use <future> all the time (in boost, no less) so it might be selection bias.

Thank you! Do you want to share the results? I could add them to the git repository or include an addendum of the post.
> most prominently, shared_ptr

Is the 'prominent' conclusion part of the analysis (I couldn't find anything in the post), or just conjecture? Anecdotally I see unique_ptr far more often than shared_ptr, but I would be interested in hard stats on which is more commonly used.

I love C++ I just wish there was some sort of way to make it memory safe. At the same time the Java stack is too heavyweight for the things I use C++ for. If there werr some sort of memory safe language with the same use cases as C++ that would be game changing.
Swift, Go, Rust, etc, all fit your description.

edit: De-Germaning

What's usw?

There are use cases for C++ that Swift and Go don't fit, due to having dynamically managed memory.

Fans of D would also put D on this list. And Ada is not dead yet!

“usw” is a common German abbreviation that means “etc.”
C++ core guidelines combined with the guidelines support library get you most of the way there.
Personally i have 3 axis for problems programming languages might be suitable for, as theres no such a thing as 'one language to rule them all' (And the 'Java cult' really made us all lost a lot of time trying to make things fit into this wrong mentality).

1 - For hardcore stuff - C++/Rust(if i can) - VM, OS, Browser, Emulator, etc..

2 - For bussiness/application/backend stuff - Swift (Go is also suitable but only if its server backend stuff, it will suck in other problem domains)

3 - for Scripting - Javascript/Lua (or Python). (This axis is expendable, as you may or may not use it)

So, from my point of view, you can also use Swift or Rust for some things, depending on the type and goal of the project, given both are memory safe and pretty fast.

You will never find 'the one true language', where you can use it for everything, so dont even loose your time trying to find it.

But for me, if theres something today that is much more closer to this goal is Swift.

Because it can cover a lot of ground. In my opinion, Rust can't do it, because given they were busy trying to eat C++ lunch in the language evolution process, they lost ergonomics and in the productivity area (you can use it of course, but you will suffer the same as if you had used C++ for the same problem domains).

Now maybe Swift will loose the Ref-counting by default runtime memory management, and if that happens, it can also be a great contender for things right now only C, C++ and Rust are suitable for.

Let me play the pedant: technically, this is an analysis, not a meta-analysis. A meta-analysis is an analysis of other analyses (typically, analyses completed by other people, and discovered through a literature review). The author has completed a single (interesting!) analysis of multiple projects.

https://en.wikipedia.org/wiki/Meta-analysis

I am the author of the article. Thanks for the kind words! You are right in pointing out this incorrect terminology. I should know better and have changed the title :)
Oh, that's great! Thank you for taking my comment constructively.
It would be nice to divide the cooccurence count by counts of respective two headers, to get a sort of correlation. Now it's quite hard to see anything beyond "most popular things are paired with most popular things" which is obvious.
Good suggestion! I will add this an update the repo/post.
A lot of the headers in the "tail" are included by the other headers, or made available in some other way, which reduces their usage. For example, std::tuple is defined in both <tuple> and <utility>, which would help explain why <tuple> is lightly used. Most of <new> is implicitly defined by the compiler. <initializer_list> is "built-in". <iterator> is generally subsumed by "auto" and type inference.
> <initializer_list> is "built-in"

You still need to include the header for any of the following:

1. Constructors (or functions) with `initializer_list` parameters.

  template<typename T> struct S {
          vector<T> vec;

          S(initializer_list<T> il)
                  : vec(il) {}
  };
2. `initializer_list` literals in range-for loops.[1]

  for (int prime : {2, 3, 5, 7, 11})
           // ...
3. Named `initializer_list` variables.

  const auto primes = {2, 3, 5, 7, 11};
  const initializer_list<int> primes2 =
          {13, 17, 19, 23, 29};
> <iterator> is generally subsumed by "auto" and type inference.

The iterator adapters[2] are still pretty useful and (I thought?) commonly used.

Also, the "range access" and (new for C++17) "container access" free functions are very useful for writing template code that works with both C arrays and STL-style containers, but they're defined in many other headers.

[1] Useful with variadic templates:

  [](auto&&... args) {
          for (auto &&arg : {args...})
                  // ...
  }
[2] http://en.cppreference.com/w/cpp/iterator#Iterator_adaptors
By "built-in", I was mostly talking about the fact that the literal syntax doesn't require a header. Case in point, here's some code I took straight from a toy AVL implementation I wrote a while back:

  auto children = {&grandparent->left, &grandparent->right};
  auto child = elvt::find_if(children, [parent](auto &child) {
  	return *child == parent;
  });
Yes, it's an abuse of initializer_list being a convenient "array-like" structure, but it does show that not every use of initializer_list needs to be used as you've mentioned. (In fact, I probably use initializer_lists more this way than in their "intended" way. I'm sure someone is going to yell at me some day for it, but nobody's done it yet).
That requires including the header. It's probably included in one of the other headers you included, but that's not required by the standard.
Yeah, it really needs to become “built-in” though.

I’ve seen some really bizarre compiler errors that tell you very little (i.e. you dared to use curly braces somewhere and somehow from that you must figure out that you need a "#include <initializer_list>").

C++ needs a standard "contrib" set of headers like Ruby. Relatively stable, but only anointed if moved into the STL.