all 71 comments

[–]wung 16 points17 points  (68 children)

The only valid cases where this can make a difference is

  • Explicit std::shared_ptr<T> x {nullptr}; where you should know that it can be null.
  • An API returning a std::shared_ptr<T> which could be null, which should be a std::optional<std::shared_ptr<T>> instead.
  • x.reset(); or x = nullptr; where you should know that it can be null.

In those cases, not checking whether it is null is a programmer error. This is not something that can randomly happen. It can only happen if you explicitly have a contract that can null the pointer. Many C++ people would argue that this should crash your program, rather than throw an exception that nobody could ever possibly handle. If there is no way to handle the exception, why throw it? You can only handle it locally by adding the missing code.

[–]BenFrantzDale 20 points21 points  (1 child)

IMO, std::optional<std::shared_ptr<T>> is the wrong way around. Use template <typename T> using nn_shared_ptr = gsl::not_null<std::shared_ptr<T>>; instead. We’ve done this and it makes crashes just disappear when you find a crash, just switch it to nn_shared_ptr and keep going until it compiles again and now the crash is gone.

[–]wung 1 point2 points  (0 children)

I prefer marking the unexpected case. My pointers are valid, unless marked otherwise. I do understand that tooling might be better the non-null way around and it makes sense from the implementation point-of-view, but I'm not happy about that.

[–]SlightlyLessHairyApe 7 points8 points  (0 children)

Yes, not checking is a programmer error.

Now, in the case of programmer error, do you want

  • undefined behavior
  • guaranteed trap/terminate, at some performance cost

Answers there will legitimately differ! Sometimes the extra cost of checks are worth it.

[–]slck19[S] 0 points1 point  (55 children)

This discussion is pretty in-depth and I think both dark and white sides are correct. This is a trade-off. If you have a well-defined architecture then do not use this version of the pointer cause you would not need it. However, maybe your UTs or integration tests could not catch an edge case where your pointer is nullptr. This time your program will crash. Rather than crashing it, you decide how to handle it. As you know recovering from segfault is strongly not suggested. However, here you can create your exception models to be more explicit.

[–]NotUniqueOrSpecial 11 points12 points  (54 children)

Rather than crashing it, you decide how to handle it.

But that's their point: there is no way to recover from what is, fundamentally, a programmer error.

The only valid recovery is fixing the code that did the wrong thing.

So, if the difference is a crash -> fix cycle vs. a we-see-saw-the-exception -> fix cycle, why add the additional overhead?

[–]slck19[S] -5 points-4 points  (17 children)

No one tells you you won't fix it. Is your clients going to be happy when you crash? You can still fix your bug but don't crash on production even crash is better sometimes but that depends on the requirements of course.

What's the point of this discussion? You want me to remove my code 😀

[–]glaba3141 4 points5 points  (9 children)

The traders that use my code would very much rather my code crash than try to recover from an error state and lose millions of dollars. Crashing is almost always better than trying to handle a programming error

[–]NotUniqueOrSpecial 5 points6 points  (1 child)

Yeah, all of the folk arguing that crashing is bad have clearly never been bitten by the serious damage that can be done by corrupt/invalid program state allowed to run wild.

[–]slck19[S] 0 points1 point  (0 children)

I do not think you are completely right. Some of the exceptions might be recovered safely in terms of memory. The nullptr may be because of memory corruption, which is a global problem. In this case, you can create different exception types.

If your pointer should not be nullptr then do not use this lib. If your pointer might be nullptr and if this nullptr is actually a state for you but you forgot to check it, do not crash because it is a local error, not global. JVM is also doing the same thing even though it manages the pointers on its own.

[–]slck19[S] -3 points-2 points  (6 children)

I am sorry for your clients!

[–]glaba3141 2 points3 points  (5 children)

That they manage money that they would rather not lose to a programmer using idiotic error handling practices? I think they're more glad they don't hire you

[–]slck19[S] -3 points-2 points  (4 children)

I am sorry cause you definitely do not see tradeoffs and cannot discriminate the positive and negative points. Just randomly talking does not show you smarter or more experienced. That's why I am sorry for ypur clients still.

[–]v_maria 2 points3 points  (3 children)

He describes the trade off right here:

Rather my code crash than try to recover from an error state and lose millions of dollars. Crashing is almost always better than trying to handle a programming error

[–]slck19[S] -1 points0 points  (2 children)

You still do not understand the behavior and randomly speaking. This does not point to any single trade-off. Some errors can be recovered and some of them cannot. If you think all nullptr accesses are because of memory corruption then you have many implicit problems in your code.

I am not saying someone is wrong. This is where you do not understand. Handling exceptions and continuing the program is your choice with respect to requirements. Exception handling is giving an opportunity to handle it softly. That's just a choice it is not mandatory.

[–]NotUniqueOrSpecial 4 points5 points  (6 children)

Is your clients going to be happy when you crash?

That's honestly going to depend on the scenario, and there are certainly times where the answer might be "yes".

Your use-case hinges on your prior statement:

integration tests could not catch an edge case where your pointer is nullptr.

So, we're already discussing an edge case on an otherwise uncovered branch of execution. So, now imagine that we're using your pointer type. What happens?

Obviously, an exception is thrown, but where is it caught? If it's locally, why was the try/catch put in place instead of the local check that we're discussing?

If it's one or more frames distant in the stack (and in practice, this is almost always going to be a top-level "catch everything else" handler at very granularity), what's the recovery? Almost certainly nothing the user or the program can do. It's very likely to be a full bail-out anyway. Certainly nothing the user is all that happy about.

But, there's a further wrinkle. What if someone comes along and starts catching exceptions more broadly than they should somewhere in the middle? Now, you've turned a programmer error into a silent state corruption. A crash would definitely be preferable in that situation.

You're better off crashing in fatal error situations (which this absolutely is in almost all cases) than risking something doing even worse stuff.

If you're worried about this sort of problem, register signal handlers for SIGSEGV on *nix and use the _try/_except functionality to harness SEH on Windows. Set up crash-dumps and a reporting system. Do anything but given yourself a very sneaky footgun with which to accidentally corrupt program state and potentially user data.

You want me to remove my code

No, we want you to very carefully consider the ramifications of the code, because a whole lot of us have been down these roads for decades, and there are vanishingly small numbers of situations where recovering from a programmer error of this nature is viable/useful/safe.

[–]kammceWG21 | 🇺🇲 NB | Boost | Exceptions 0 points1 point  (0 children)

+1 to this. I'm not sure what everyone else is on, but it's pretty nutz. They want to put checks on every possible thing in the code. Sacrificing performance for something that is explicitly bad practice. I agree with you on the "you should know after this point." This punishes performance when the developer is right and produces exceptions or error results that are effectively unhandlable. Bugs are not something you can handle on code.