Hacker News new | ask | show | jobs
by jmgao 2761 days ago
I disagree with a lot of the Google C++ style guide, but I disagree with many of the complaints in the post.

> Although GSG does not forbid them categoricaly, it says to “Avoid using forward declarations where possible”. However forward declarations are useful in contexts like the following: ...

Yes, those are the cases where it isn't possible to avoid forward declarations, so it's fine to use them?

> While it’s true that initialization/destruction order of globals between translation units is not defined and can pose problems, this rule is overly prohibitve. Globals like this are not problematic and very useful:

No, globals like that are very problematic, so much so that there is a clang warning specifically to detect these (-Wexit-time-destructors). exit runs global destructors, so if one thread calls exit while some other thread is still doing stuff, it might destroy an object that's in use, leading to explosions. Also, if you're exiting, why are you paying to deallocate memory when it's all going to be blown away anyway?

> The lynchpin of C++ are value-types. Such types should be copyable and moveable and the language automatically generates the necessary constructors and operators by default. “a copyable class should explicitly declare the copy operations, a move-only class should explicitly declare the move operations” – this goes against the language philosophy.

It is very non-obvious which constructors will get automatically generated, because it depends on the types of the members. `Foo(Foo&& move) = default;` makes it immediately obvious.

6 comments

The most egregious thing for me is:

> in other words, the reader is expected to understand the semantics of an unknown function without consulting the declaration (or documentation).

The idea that the author considers making code as self-documenting as possible at the call site a conceptual mistake is just weird to me.

> No, globals like that are very problematic

Indeed, they're terrible. What I don't understand is why basically every Google-authored project has loads of these (protobuf, gflags, TensorFlow...)

In Chromium they are so widespread that makes the rule showing that complex code cannot avoid them and defeating the rule.
Same sentiment here. Many of GSG constraints are good and others are purely because of legacy code at Google. So don't take GSG as religion and implement it to the letter ask why this constraint is proposed and does it apply to my code?

> GSG forbids exceptions on the ground of “Google’s existing code is not exception-tolerant”.

Unless you are doing embedded software, you should use exceptions. I have tried this "modern" trend of returning instead of raising a few times and I'm not convinced at all its less bloated or less error prone way of doing things.

> The lynchpin of C++ are value-types. Such types should be copyable and moveable and the language automatically generates the necessary constructors and operators by default.

It generates constructors and operators by default. They may not be the ones you want, though. They will satisfy the compiler, but they may do the wrong thing. If the default thing is the right thing, then yes, "= default" makes it obvious. If you don't say, then it isn't obvious whether the default is right, or the default is wrong but you missed it.

> Yes, those are the cases where it isn't possible to avoid forward declarations, so it's fine to use them? But my point was that it _is_ possible to avoid them by type-punning via void*. Now, one can argue that it's unreasonably bad way to avoid them. But will a reviewer following this guide make the same assumption?

> exit runs global destructors, so if one thread calls exit while some other thread is still doing stuff, it might destroy an object that's in use, leading to explosions.

Or you can structure your code in a way that all your threads join prior to main exiting. Never call exit() or _exit() or even pthread_exit(). I think it's much cleaner. Not to mention that not every program is multi-threaded.

> It is very non-obvious which constructors will get automatically generated, because it depends on the types of the members. `Foo(Foo&& move) = default;` makes it immediately obvious.

Agreed but can you imagine what a simple

struct Point { int x, y; };

become if we were to make everything explicit?

> Yes, those are the cases where it isn't possible to avoid forward declarations, so it's fine to use them?

The author explicitly mentions how it is possible to avoid forward declarations in those cases: "both of these forward declarations are possible to avoid by type-punning through a void* but it is not a good pattern."

By definition, if avoiding something isn't possible, then there cannot be a "how to avoid".

Why even mention a non-starter choice, like using type punning through a void pointer, as an alternative to a forward declaration.

If a construct is "not possible to avoid" that generally means "not possible to avoid without changing the structure of code and run-time data (let alone changing them to something unsafe)".

Gratuitous forward declarations of C++ classes are "bad" for various reasons. Such as: people sometimes write them just to shut up the compiler, instead of including the right header to provide that declaration. "I'm just using a pointer to this; what's the harm."

It makes perfect sense for a coding standard to advise against this.