> high-reliability
I never heard this term used with C++ before. Can you explain more? Ideally: Can you give an example snip of code that demonstrates it clearly?[–] arein3 4 days ago
[–] weitendorf 4 days ago
I wrote a comment explaining what in my mind distinguishes "high reliability" (defensive programming and making invariants/error cases/intended use explicit) C++ from regular C++. IMO it basically boils down to contemporary "Google/fintech style C++", which is a different development culture and ecosystem from defense/automotive
[–] fpoling 4 days ago
[–] NSUserDefaults 4 days ago
[–] Cthulhu_ 4 days ago
(I'll just pre-empt that if you say "Rust", Rust wasn't around until recently and the F-35 project started in '93. These things span decades, while only a few programming languages do)
[–] LiamPowell 4 days ago
[–] fpoling 4 days ago
[–] tialaramex 4 days ago
Bjarne's 2005 JSF rules have the same effect as an "Employees must wash hands" sign. It's not the billion dollar fast food chain's fault that a minimum wage employee didn't obey the sign and now you've got food-borne illness. They put a sign up!
[–] pjmlp 4 days ago
[–] SolarNet 4 days ago
Boeing and other defense contractors dropped it because C++ programmers were cheaper on paper (not realizing/caring that good C++ engineers were more expensive).
[–] cestith 4 days ago
[–] amelius 4 days ago
This also sounds pretentious ...
[–] NSUserDefaults 4 days ago
[–] fps-hero 4 days ago
The only other legitimate choice for writing software at the time would have been ADA, which for some reason never took off despite being purpose build for the application. Of that time, C++ was the new hotness, it was popular, it was solving architectural problems, and it was performant, unlike say Java.
It’s fairly naive to say “I’d never use C++”. The C++ programmers of the time would have looked at Fortran or COBOL with the same level of disdain. We are lucky to live in a time of better options, and the impending post code future.
[–] tialaramex 4 days ago
Ada makes a lot more sense for this work than C++ did twenty years ago.
[–] bluGill 4 days ago
[–] NSUserDefaults 4 days ago
In today's terms, that's like vibecoding a Mars mission vehicle just because it's "cool".
[–] drysine 4 days ago
What do you mean?
[–] fps-hero 2 days ago
[–] AnimalMuppet 4 days ago
[–] pjmlp 4 days ago
[–] rerdavies 3 days ago
[–] throwaway2037 2 days ago
[–] fps-hero 1 day ago
The F35 program certainly seemed like a white elephant, until it crossed the threshold into production and wasn’t. It was a massively delayed cost blowout project, which has turned into a huge success.
[–] AdamN 4 days ago
[–] weitendorf 4 days ago
Here's an example that uses compile time type assertions (static_assert, C++ can already do this), runtime errors (CHECK, standard Google/abseil practice for explicitly handling invalid states and invariants), test/dev checks (defensive programming, basically an inline test), and wrapper/error handling types to do what you might want contracts to do:
template <typename T>
absl::StatusOr<double> Percentile(std::span<const T> sorted_values, double p) {
static_assert(std::is_arithmetic_v<T>, "T must be numeric");
CHECK(p >= 0.0 && p <= 100.0) << "p out of range: " << p;
DCHECK(std::ranges::is_sorted(sorted_values)) << "input not sorted";
if (sorted_values.empty()) {
return absl::InvalidArgumentError("empty input");
}
const std::size_t last = sorted_values.size() - 1;
const double rank = p / 100.0 * last;
const auto lo = static_cast<std::size_t>(rank);
const std::size_t hi = std::min(lo + 1, last);
const double result = std::lerp<double>(sorted_values[lo], sorted_values[hi],
rank - lo);
DCHECK(result >= sorted_values.front() && result <= sorted_values.back());
return result;
}
(function impl is AI, only need it as scaffolding to demonstrate the contract-adjacent, non-implementation code)Before I was exposed to this style of C++ and learned to write it, C++ basically felt like a different language. People say that the C++ standards committee and language designers have different priorities and developer culture vs most big tech companies/finance and I think it's because even if they're aware of the features and tools that come out of Google (et al) [1], they don't use them in their current day to day jobs and want that functionality in the core C++ toolchain.
IMO, a lot of what they want to add is already "solved" by third party tooling, but because for whatever reason they don't use those as part of their everyday work, they sometimes come up with language features that overcomplicate or solve the wrong thing, in a way that you would intuitively understand if you were already regularly using the 3P tools that solve them.
I could also be the one misinterpreting things here so apologies if this is obvious or not what the original commenter meant by "high reliability".
[0] https://en.wikipedia.org/wiki/High_Integrity_C%2B%2B
[1] https://abseil.io/about/ one of many third party ways to make C++ less insane
[–] pedro_caetano 4 days ago
At this point the rust contracts crate with requires/ensures looks like a more straightforward approximation of Ada's Contracts and assertion policy. But still as far as I am aware no one has implemented something that mimics the static proof functionality in Ada's SPARK subset (which arguably not all applications or problem domains actually need or can justify the extra effort/cost).
[–] rramadass 4 days ago
[–] weitendorf 4 days ago
double safe_sqrt(double x)
pre(x >= 0.0)
post(r: r >= 0.0)
{
return std::sqrt(x);
}
Preconditions violate the principle of "make invalid types unrepresentable" but look like they're helping. They are basically a oneoff extra specific type that (if the state you're checking is worth checking at all, it must be representable in the actual argument's type) contradicts the actual argument's type. So why not change the argument's type directly?Because it's hard, or not possible, for the same reason that most of these preconditions aren't generally statically verifiable.
Suppose your precondition is x > 5. Statically, you can stop someone from directly passing a literal <= 5, detect locally declared variables assigned to numeric literals and do the same. But to generally infer whether any argument satisfies specific properties quickly gets extremely complicated, because how can you generally verify that the calculations done to a const or literal within the caller's scope guarantee certain properties about the output, without actually performing the calculation? You can come up with cases where you only add to an int and you see it was initialized to 0, so you know it's never < 0 barring underflow. Those are the easy cases
If the caller is passing in something derived from their own arguments, or from I/O, all bets are off. All you know is that the argument has a type, and its value was derived from unknown inputs. So you need a runtime check to actually assert your precondition and make it behave like a type check. But then what happens if input doesn't meet the precondition? Do you throw an exception or crash?
I think that there should be a reference implementation for a useful static analyzer for preconditions, for it to be added to the language. Obviously it would be nice if you could verify that callers only pass in x > 5 to your function, but I think deferring the semantics for contracts to however the build/compiler chooses to handle them (because they might, one day) is not good enough for adding a new feature and syntax to a language. If preconditions can be used for useful static analysis beyond what static asserts and the type system gets you already, there should be evidence for that. I'm skeptical that you can do any better than a regular static assertion/type checks.
Also, I do think the lack of explicit handling (if in practice preconditions are only generally evaluable at runtime) in the contracts syntax is bad. The proposed syntax looks too much like a type or static assertion for a runtime check (note that you can easily see what CHECK/DCHECK macros actually do just by clicking through to their definition). And if it isn't in practice just a runtime check, there should be proof!
[–] William_BB 4 days ago
This is the part of contracts that I don't understand. I suppose you could (1) runtime validate any user input to the program and then (2) chain multiple functions like `safe_sqrt` that provide pre and post conditions.
In theory, this should allow the static analyzer to reason about any arbitrary input. However, this to me raises even more questions: is this practical, is this possible and why would I not try to achieve the same through types ("make invalid types unrepresentable"). Maybe I'm completely off the mark here.
[–] weitendorf 3 days ago
If they can't, then it is IMO a mistake for the language to make contracts declarative (like types), or to think of the static checks as anything more than something extra on top of what is actually a runtime check. And if they can, then why are they not just types?
Bad idea to assume "someone will write static analyzers", and use that to justify a language feature capable of expressing much more complex logic than any static analyzer would be capable of handling.
[–] rramadass 3 days ago
See also my comments here - https://news.ycombinator.com/item?id=50365641 and https://news.ycombinator.com/item?id=50375318 (this one explains how static analysis works which is the confusion i see above).
The idea of a "proof" which is what Symbolic Execution by a Static Analyzer does is different on both sides of the above isomorphism. You cannot mix them indiscriminately. This is because C++ does not have a typing theory (in the sense of lambda calculus derivatives) but simply maps types to sets. Hence one cannot do "Type-Driven Design" natively like a lambda calculus-based language even though you can do aspects of it via explicit programming techniques (eg. see "Functional Programming in C++ by Ivan Cukic").
That is the reason i suspect contracts in C++ moved away from the original focus on static analysis to managed runtime checks + some static analysis. C++26 contracts are firmly in the Set Theory/Predicate Calculus camp and are not integrated into the Type System.
We need an augmentation of the C++ type system and then a narrowing so we can end up with something like Ada-SPARK (call it "Safe C++"/"Profiles"/whatever).
[–] weitendorf 1 day ago
I didn't mean to claim type systems/static analysis in general are not mathematically sound, but that bolting "static analysis" on to something obviously mathematically unsound doesn't give you actual types. A language allowing you to express new invariants and runtime guarantees it can't enforce (not just because they're unimplemented, but because what an implementation would need is not even expressible) is more inconsistent than one where you can't make unenforceable claims
You basically need a complete LEAN-style predicate calculus to generalize guarantees on runtime invariants through compile-time analysis. You are making claims on the interior structure of data that is defined by how many bits of information it encodes, including at the boundary where provenance ends (the arguments passed into your binary are all basically just multiples of 4/8byte enums, ints, strings, or floats), where you'd also need to be able to check/enforce that the data has particular properties, otherwise all you did was introduce more ways to do UB
C++ has nothing close to this in place and it's in fact quite a different claim that any particular invariant is obviously statically verifiable, or that a large class of assertions would be very easy to prove statically, than the claim it would be fine to only catch "most" or even "many" type assertions statically and just handle the rest at runtime.
It's like saying "well 2 + 5 obviously < 10 and most static checks are obvious, so it's better to at least handle that even if we can't statically prove which particular subset of N have 2^N % 10 = N and just check the really hard stuff at runtime". Because that means you have to ask whether any runtime failure is some emergent/adversarially difficult problem from number theory, but at least you can catch all the easy stuff you would have caught anyway
[–] throwaway2037 2 days ago
In the code sample above, can you explain why DCHECK is used instead of CHECK? I needed to Google to ask about those macros, but it looks like DCHECK is only enabled in debug builds.
[–] zaphirplane 4 days ago
[–] Rexxar 4 days ago
[–] Maxatar 3 days ago