Subject: Re: [boost] Looking for thoughts on a new smart pointer: shared_ptr_nonnull
From: Matt Calabrese (rivorus_at_[hidden])
Date: 2013-10-07 01:56:28
On Sun, Oct 6, 2013 at 9:34 PM, Gavin Lambert <gavinl_at_[hidden]> wrote:
> On 10/7/2013 4:37 PM, Quoth Matt Calabrese:
> If you are violating your precondition, throwing is not a proper solution
>> for the reasons already explained several times at this point. Why the
>> non-nullness of the pointer should be a precondition was also already
> I have yet to see anything that convinces me that there is a valid reason
> for that. Maybe I missed it, or maybe we just don't agree on this aspect
> of code design. :)
I think you either missed or didn't get it.
Here's an actual solution. Don't violate your function's precondition and
>> therefore don't have UB.
> That's nice to say in laboratory conditions. Try doing it in any
> real-world application without *something* validating your preconditions
> and see how far it gets you.
Someone /is/, or should be, validating your preconditions (it's you). That
is precisely what a precondition is.
Failing early and fast on programmer errors *when the programmer is around*
> is the most important thing. Being able to keep the system running
> (without UB) in actual usage by detecting and recovering from unexpected
> failure is the second most important thing. (Reverse the priorities of
> these if you are the customer.)
Again, if you can keep the system running in the case where you would be
passing the null pointer, then it is your responsibility to do that.
> (Note that I'm not talking about "catch exceptions everywhere and maybe
> log them if you're lucky" type 'recovery', but *real* recovery at
> appropriate points that know how to repair state and resume normal
If it's possible to do real recovery in cases where you /would/ pass a null
pointer, then it is your responsibility to be either immediately handling
the situation or throwing your own exception.
I just don't understand why you don't seem to think that the constructor of
> the "guaranteed not null pointer" is a reasonable (and essential) place to
> verify that condition, for the sake of code sanity.
Your sanity check is the assert.
Pushing the responsibility higher up the call chain just increases the
> chances that someone is going to forget, and *introduce* UB. That's not
> the direction you should be going.
We're not pushing the responsibility to meet preconditions /up/ at all.
It's exactly where it should be (some point prior to calling the function).
We're talking about avoiding pushing the responsibility /down/ into a
function where the precondition was already violated (where we would
already be in UB land).
> So in other words, you are writing a hypothetical "handler" that you've
>> never even seen triggered/tested in a debug build (because it can't even
>> get there in a debug build) and this handler exists because you might have
>> some way to handle the null case and it is simply inexplicably a better
>> choice than not violating a function's precondition and relying on UB? You
>> don't see any flaws in this logic?
> No, I don't. You don't run tests on your release builds too?
When did I say that I don't run tests on release builds? The point is you
are knowingly introducing a completely separate code path that only occurs
in release builds, that is only encountered as a part of UB, and that you
are claiming is perfectly valid and to be relied upon. If you somehow have
the ability to handle this case, why would you not want to be handling it
and testing it in a debug build? As well, if it's a valid code path, then
you shouldn't be relying on UB to get you there. In other words, just
validate your precondition and handle it if it's not met.
> And nothing is relying on UB. The exception is thrown *before* UB occurs.
No, it's not thrown before UB occurs. By having a function with
preconditions and calling that function with values that don't meet those
preconditions, you have undefined behavior. If you want specified behavior,
then it's not a precondition. That said, I've already also explained why it
should be a precondition. Please, let's not turn this into a vector at().
> Yes, it is a programmer error. People forget things. It happens. When
> it's an easy mistake to make, and easily avoided (via exception)
An exception does not "avoid" anything. The mistake is still made and it is
still a bug.
, why not defend against it rather than simply allowing the code to enter a
> UB path? UB is bad. Don't encourage it.
No one is encouraging UB.
And it's not using exceptions as control flow, because it's not a normal
> execution path.
I challenge you to define what you even mean by this. Do you mean that the
code path is simply not common, and that this somehow makes things
acceptable? There are plenty of branches in code that are not "normal."
-- -Matt Calabrese