Hacker News
points by jandrewrogers 4 days ago | 0 comments
As someone who writes high-reliability C++ but is completely outside the standards process, I see the issues with C++ Contracts. It isn’t clear that it solves a problem that anyone has in real codebases, especially after C++20 concepts.

If you were already writing high-reliability C++ then the benefits are difficult to quantify. If you weren’t writing high-reliability C++ then it saves you almost nothing over doing the standard idioms of high-reliability C++ that were already available to you at very marginally more cost. It does standardize expression of it in narrow cases but the payoff seems minimal.

Most of what C++ Contracts delivers is a mashup of what you can already do with constexpr and C++20 Concepts. I like reliability features but this feature, as delivered, looks fairly useless if you already use reliability features. A compelling standard would need to make much deeper guarantees out of the box. And the compiler can already see through most compositions of constexpr and C++20 concepts, which is already very expressive.

C++ Contracts looks a bit like a feature that was designed long before current standard features existed.

[–] throwaway2037 4 days ago

    > 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?
[–] zaphirplane 4 days ago
Top 2 Google search result are ..: this post and this article
[–] Rexxar 4 days ago
Google give enormous priority to new content, especially on very well referenced and active sites like HN. So it probably doesn't mean as much as you think.
[–] Maxatar 3 days ago
I can search for many technical terms that have recently been posted to HN and it and appear in the comments and it won't come up on the front-page, let alone the first two items.
[–] arein3 4 days ago
Its a set of rules, used by defense software, they are available on the internet
[–] weitendorf 4 days ago
I think you're thinking of https://en.wikipedia.org/wiki/High_Integrity_C%2B%2B

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
The biggest split is not using exceptions with aborts on memory overflow. It alters the programming style to such extent that it feels like a different language.
[–] NSUserDefaults 4 days ago
For example the failed F-35 project used it. Seems pretentious and risky compared to using a language that is safe by default.
[–] Cthulhu_ 4 days ago
What language do you propose?

(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
The incredibly obvious answer is Ada, given the DoD commissioned it and the fact that it was used for the F-22.
[–] fpoling 4 days ago
I wonder if DoD desire to tap into a bigger pool of C++ programmers also brought a lot of people who was not trained to write a high-reliability code.
[–] tialaramex 4 days ago
C++ is a pre-standard "Eh, just do whatever" language in 1993.

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
He always misses the amount of avionics bugs that the F-35 suffered from, in spite of the rules.
[–] SolarNet 4 days ago
Ada is far safer than Rust (not in edge case memory allocation problems, but in day to day application programming), and not only existed at the time, it was the industry standard for aviation (for good reason). Entire educational programs and pipelines existed for it.

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
One of my current coworkers used to work on on-board systems for military aircraft. Anecdotally he tells me the critical systems for the ones he worked on were Ada but other systems could be C++.
[–] amelius 4 days ago
> compared to a language that is safe by default.

This also sounds pretentious ...

[–] NSUserDefaults 4 days ago
Because you think there is no such language, or even one that gets closer than C++? Look I still kind of like C++… for games. I would never put it in a fighter jet for gods sake.
[–] fps-hero 4 days ago
I don’t want to be inflammatory, so let’s stick to facts. The F35 is the manifestation of a test aircraft designed in the 90s. Of that era, software was predominantly single threaded, hardware definitely so. They also weren’t using the latest and greatest of hardware at the time, they opted for proven flight platforms with hardware dating back well back into the decade. So, very lacklustre performance, tight memory constraints.

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
> The only other legitimate choice for writing software at the time would have been ADA

Ada makes a lot more sense for this work than C++ did twenty years ago.

[–] NSUserDefaults 4 days ago
“Let’s use the new hotness for this new fighter plane” maybe puts things into perspective for me.

In today's terms, that's like vibecoding a Mars mission vehicle just because it's "cool".

[–] drysine 4 days ago
>it [C++] was solving architectural problems

What do you mean?

[–] AnimalMuppet 4 days ago
In what sense was the F-35 project a failure?
[–] pjmlp 4 days ago
Total cost due to avionics issues and other construction failures.
[–] rerdavies 3 days ago
And failure to meet project requirements.

e.g. https://responsiblestatecraft.org/f-35-failure/

[–] throwaway2037 2 days ago
Has there ever been a modern fighter jet built in the last 50 years that wasn't consider in one aspect or another an "failure"? At this point, the term "failure" seems almost pointless to describe very large, nation state projects that are heavily used and have thousands built.
[–] fps-hero 1 day ago
Whether the F35 was a failure depends on whether you are looking out your forward window, or rear view mirror, and when you were looking.

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
Username checks out :-)
[–] weitendorf 4 days ago
Assuming the original commenter is referring to essentially best-practices, defensive C++ like you see at Google and projects like Envoy proxy, and the other reply is prob mixxed up and thinking of high integrity C++ [0],

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
If you squint hard enough your example does look a lot like Ada pre and post condition contracts (perhaps a bit less ergonomic, at least for someone who hasn't written C++ in a while).

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
> IMO, a lot of what they want to add is already "solved" by third party tooling,

https://news.ycombinator.com/item?id=50364593

[–] weitendorf 4 days ago
I haven't seen any good examples of contract preconditions that I would actually want to use. What I've seen is stuff like

  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
> If the caller is passing in something derived from their own arguments, or from I/O, all bets are off.

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
No I completely agree. I think it is almost a deus ex machina/wishful thinking where people wish type systems could handle invariants like this, and know at least some invariants should be easy to enforce statically, but haven't seriously considered whether all invariants generally can be feasibly statically verified at buildtime.

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
Nice. However, you are mixing up both sides of the "Curry-Howard Isomorphism" i.e. Set Theory/Predicate Calculus vs. Lambda Calculus/Type Theories i.e. Classical Logic vs. Constructive Logic.

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 think we are maybe making the same point in opposite directions.

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
This is a great reply. Thank you for the code snip. It makes a lot of sense to me now. Basically: You need to have pre-conditions and post-conditions.

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.

[–] drivebyhooting 4 days ago
Your comment just made me realize contracts is different from concepts (which are basically bounded genetics, right…?)
[–] jandrewrogers 4 days ago
C++ concepts evaluate the properties of types at compile-time. You can inject arbitrary constraints on type properties anywhere in the code including conditional codegen. It is a way of asking if a type “can express this complex set of abstract things or has this enumerated set of properties” with almost arbitrary detail. They are extremely powerful in terms of expressiveness and specialization of code. Insanely good for performance and reliability engineering.

One of the best features to be added to C++ in many years.

C++ contracts are an attempt to bring C asserts into the C++ age. The weakness of C++ contracts is that they are mostly syntactic sugar over C asserts. If you really care about this type of thing, you probably already have a custom C++ assertion framework that is at least as capable if not more.

The promise of C++ contracts is that they can aid formal verification, which would be great. As delivered, it can’t do much more than was already possible, which will inhibit adoption.

[–] amluto 4 days ago
I maintain a codebase that I’ve migrated from C++98 to C++11 and all the way to C++23 and even a bit of draft 26 (the latter for some minor library improvements). Few things made me quite as happy as moving from using SFINAE to using requires clauses and concepts.

(IMO the single best improvements over the years have been atomics and maybe variadic templates.)

[–] jandrewrogers 4 days ago
I think you are right to position atomics and variadic templates as among the most consequential features of C++ in the last couple decades. It made an enormous number of things possible.

One thing I give the C++ feature gods credit for is recognizing material deficiencies in systems languages and unapologetically trying to fill those gaps starting with C++11. The addition of atomics, strict variadics, constexpr, lambdas, concepts, lifetime annotations, etc has been transformative for C++.

[–] aw1621107 3 days ago
> lifetime annotations

Say what!?

[–] throwaway2037 4 days ago

    > the single best improvements over the years have been atomics...
Of course, libatomic and friends have existed for a long time. Real question: Do you feel that the standardisation of atomics within the C++ base library is a material advantage over using libatomic? Also, maybe there are atomic APIs in the C++ base library that are better than libatomic. If so, please kindly share an example or two. I would like to learn more.

EDIT

I forgot to give a hat tip to your big upgrade from C++98 to C++23. In some projects, that can require an enormous amount of effort! Can you share a "stories from the trenches" that were difficult in that upgrade?

[–] bluGill 4 days ago
Standards work best when they can take a bunch of existing slightly different implementations and pick the best of them all, with input from people who understand the compromises, and with input from the real world experience of the different ways those compromises work out in the real world.

Atomics could work that way. However many other things cannot - we knew modules were a good idea, but they are a everything on a project must use them before anyone can benefit and so they were forever doomed to not be in widespread use until there is a standard. Even today we see adoption is slow because while there is a standard that works everybody is waiting on some other part to adopt before they can get any benefit. (and those who need to go first see the least benefit), but there are signs that things are moving.

[–] pocksuppet 4 days ago
libatomic is a GCC implementation detail that implements atomic math operations that are not available on the target platform by using non-atomic math and compare-exchange. Are you sure that's the one you mean?
[–] Shorel 4 days ago
I want to read your blog describing these experiences.
[–] AlotOfReading 4 days ago

    The weakness of C++ contracts is that they are mostly syntactic sugar over C asserts. 
The sugar is actually one of my issues with them. The evaluation semantics are much more complicated / potentially slower than C asserts because of the sugar, and why you would have to pay those costs isn't obvious at first glance. Lisa Lippincott's excellent talk on the design of contracts [0] is what switched me from a sort of neutral support to disliking it. I think it's a stupid place for the national bodies to draw the line though. std::regex is acceptable, but a nothingburger like contracts tanks the vote?

[0] https://www.youtube.com/watch?v=yhhSW-FSWkE

[–] jandrewrogers 4 days ago
To be clear, I have a lot of issues with the contracts proposal. I probably wouldn’t use despite living out on the standards frontier. I don’t even use C asserts already because they are not expressive enough in important ways. This proposal kind of reifies the inadequacies.

I think the risk is that C++ contracts become an evolutionary dead-end that hang around forever in code bases because they were standardized. It wouldn’t be the first time this has happened.

[–] randomNumber7 4 days ago
What do you do instead of C asserts? Throwing exceptions?
[–] stingraycharles 4 days ago
Concepts are evaluated compile time and make a lot of TMP more elegant. Contracts are runtime check, akin to assertions.

Phrased differently, contracts are to concepts what assert is to static_assert.

[–] rerdavies 4 days ago
I think the wumpus that's being chased is that contracts COULD evolve into a compile time check. I'd be much happier if they actually caught the wumpus before they start polluting the language with yet another baroquely complicated feature that gets abandoned before it actually becomes useful (e.g. coroutines, and modules). Or just keep it simple and give us a C++-sensible replacement for assert(), and avoid the rest of it. i.e. contract_assert without the word "contract" in it. That would be a nice feature.
[–] bluGill 4 days ago
Today the compiler is allowed to fail to compile any code that will break a contract. However the details of what this means is very complex, and the ability of compilers to detect things that break the contract at still evolving. Thus forcing anything on a compiler would force an easy subset on the compilers while shutting down innovation in what we can do in the future.
[–] MFHava 4 days ago
Funny how often I read online that Coroutines are abandoned/useless, yet we ship products based on them and their application in our codebases grows continuously...
[–] rerdavies 3 days ago
Sure. But you would have had to use either a 3rd party coroutine library, or have written one yourselves to use coroutines.

C++20 provides the language feature only, and none of the libraries to use the feature. You can write your own library to use the C++20 feature, but the standard APIs are raw and brutally complex. There are 3rd party libraries to use coroutines, but they are not part of the C++20 standard.

C++23 provides a std::generator, which is indeed a great feature.

Finally, C++26 provides std::execution, and std::task, which finally makes the feature usable with only standard C++ feature, and provides facilities for executing coroutines on thread pools, or message pumps. Significant features and problems have been deferred to C++29.

I guess it's a fair point that coroutines have not been abandoned. But it's alarming that it will now take the better part of a decade for the feature to reach a sensible state (depending on how C++26 gets handled).

[–] MFHava 2 days ago
> You can write your own library to use the C++20 feature, but the standard APIs are raw and brutally complex.

I wouldn't put it that way, but I've written several coroutine wrapper types at this point, so my perspective is certainly not objective here.

> Finally, C++26 provides std::execution, and std::task, which finally makes the feature usable with only standard C++ feature, and provides facilities for executing coroutines on thread pools, or message pumps. Significant features and problems have been deferred to C++29.

I remain unconvinced mere mortals will use std::execution.

It's a marvellous example of proving what is possible with TMP without needing language support.

Naturally, it's template hell - heck, the main designer even wrote a Visual Studio Extension to understand the cryptic error messages it can generate.

Apart from that: it interacts badly with Coroutines. Even though the author of std::execution::task reached quite a good design (there are some warts, some of which could only be fixed by delaying to C++29 as it needed a change to the language feature), std::execution has poor support for it - which is unsurprising as key features of coroutines (like symmetric transfer) are not even discussed in P2300...

[–] rerdavies 2 days ago
That's disappointing. :-( Fwiw, as I remember it, the message pump implementation was easy enough. presumably because it doesn't require symmetric transfers. The thread pool implementation was hell.
[–] mikepurvis 4 days ago
Too bad it couldn't have been contract and static_contract.
[–] YuechenLi 4 days ago
A concept is a compile time requirement that bounds templates which are the generics in C++, so you are close.
[–] rramadass 4 days ago
Concepts are for Types at compile time while Contracts are for Values at runtime.
[–] pjmlp 4 days ago
The problem is that the current design doesn't take into account binary libraries, or what happens when conflicting contract modes are linked together, as contracts aren't reflected on ABI.

Additionally they offer very little mechanisms versus Eiffel's original design, Ada/SPARK or Frama-C.

A basic MVP, expected to improve on subsequent C++ version, assuming that the proposal authors stick around for those evolution proposals.

[–] throwaway81523 3 days ago
I don't understand the controversy or objections, though maybe I'm missing something. This looks like Ada-2012 contracts which evolved from SPARK Ada going back to near-ancient times. They're well accepted I thought. The idea is to standardize the syntax so that static analyzers can check the contracts, like in Ada. I think Rust is getting something similar. It seems important. Maybe the DIS was rejected because there are still disagreements about some details, ok fine. But rejecting the whole feature sounds counterproductive to keeping C++ relevant. Assuming that last is a good thing, of course.
[–] steveklabnik 3 days ago
Those are evaluated at compile time, C++’s aren’t guaranteed to be, they’re runtime checks.
[–] throwaway81523 3 days ago
Yes, static checking when available will have to figure into the toolchain so the compiler knows what to do. It's similar with Ada right now.
[–] quotemstr 4 days ago
Huh? Concepts are types of types. Contracts are fancier, structured runtime assertions. What does one have to do with the other? They're nothing alike.
[–] taybin 4 days ago
They both start with the letter C, have two syllables, and have/had a hard time getting into the standard. If you’re not paying close attention, it’s easy to get them confused.
[–] moomin 4 days ago
I’ll admit I only learned they were different today. I’d like to think that if I were a regular C++ user I’d be on top of this, though.
[–] jandrewrogers 4 days ago
That’s exactly my point.

Contracts notionally offer the ability to lift some runtime assertions to compile-time. That is how it was sold. Except they don’t in practice.

Meanwhile, concepts + constexpr if used artfully deliver that in practice, including for many cases that contracts were supposed to solve.

I push all verification to compile-time to the extent possible.

[–] quotemstr 4 days ago
I don't buy the idea that in "high-reliability C++" every condition worth checking can be checked statically, not unless you define "high-reliability" vacuously as that set of programs for which the claim is true. Real-world programs have legitimate reasons to verify invariants dynamically, and C++26 contracts provides an improved way to do so.

While the C++26 variant of contracts doesn't provide the benefits of its static-analysis-centric ancestor from 2014, and therefore isn't as consequential a feature, it still provides value to some and is absolutely not important enough to be worth killing a standard. Concepts do not provide this value.

In any case, it's simply false that C++26 contracts are unnecessary because we have concepts, yet this claim is the thrust of your initial comment.

[–] jandrewrogers 4 days ago
The issue is that contracts are less flexible than the existing capabilities in practice, it is limited syntactic sugar.

Contracts were sold with the idea of aiding compile-time verification, a noble purpose. The combination of concepts + constexpr allows you to do deeper compile-time verification today by other means than contracts can check. It isn’t obvious that this will ever not be the case. This means contracts is relegated to being another flavor of C asserts.

C++26 contracts are arguably unnecessary because we can achieve the same results other ways, sometimes with stronger properties. I’m a proponent of features that clean up the syntax but C++ contracts look like a nod to people that still use legacy C asserts. That’s fine but it also perpetuates an inadequate model of what assertions should be capable of expressing.

People figured out how to do with C++20 better than what contracts promised.

[–] quotemstr 4 days ago
There's nothing "legacy" about checking invariants at runtime, and you can't simultaneously dismiss the concerns of people doing so on the grounds that they're "still us[ing] legacy C asserts" when you deny them an alternative to "legacy C asserts". That's question-begging.

> People figured out how to do with C++20 better than what contracts promised.

What contracts used to promise is irrelevant. The contracts on the table modernized runtime assertions and are irrelevant to concepts, constexpr, and related facilities.

Is the contracts feature more useful than C's assert()?

If you want to argue that we're better off with the old macro, do so directly. Concepts, in this context, amount to irrelevant distraction.

[–] jandrewrogers 4 days ago
I have no issues with people check invariants at runtime. It is unavoidable. C++ contracts is a mediocre way of doing it. Many C++ code bases don’t use any macros at all for assertions and haven’t for a long time. Their models are much more nuanced than C++ contracts allows.

Why would anyone that already ditched C asserts use C++ contracts when it can’t express what the alternatives already can? This isn’t a new problem. People were creating more expressive assertion replacements than contracts offers a decade ago.

C++ contracts are nerfed. It is less expressive and capable than what anyone that cares about these things already uses. So who is expected to use it?

[–] rramadass 4 days ago
> Why would anyone that already ditched C asserts use C++ contracts when it can’t express what the alternatives already can?

You have not understood the C++26 contracts proposal.

It introduces three new syntax viz. "Pre" and "Post" in function signature declaration and a separate "contract_assert" which you can use anywhere in the code path.

It is the "Pre"/"Post" that is useful here. Since it is part of function signature declaration, a static analyzer can easily make sure that all function calls satisfy the contract else you get warning/error. The compiler on its part can inject appropriate code at the function boundary points to do runtime checking etc.

You cannot do this with assert macros. If you wanted something like this earlier, you would have had to change the compiler to emit modified function prologue/epilogue and build your own contract framework which few people had the capability to do.

You can now see the immediate benefits particularly for standard library (and other) code hardening needs. Many UB circumstances can be easily flagged at compile time without too much complexity.

Finally, note that if you use compile-time switch "ignore" to turn-off everything, only the runtime checks are elided (so you do not lose any performance) but the static analyzer can still catch all possible inconsistencies at compile-time thus giving you the best of both worlds.

[–] rramadass 4 days ago
> While the C++26 variant of contracts doesn't provide the benefits of its static-analysis-centric ancestor from 2014

Do you have any information as to why they shifted the focus from static analysis to runtime checks? I only remember that it was based on some Microsoft annotation language or something.

[–] bluGill 4 days ago
Static analysis is hard. C++ 26 contracts are supposed to be the minimum viable feature that can be expanded on in future revisions.

Already the compiler is allowed to refuse to compile any code if it would guaranteed fail the contract. The next step is to see how far we can go with that static analysis and what those static analysis tools need to be better. However, this requires people having contracts in fairly widespread use so they can start trying their new ideas to see what happens when they hit the real world.

[–] rramadass 4 days ago
The original idea for C++ contracts was to implement something like Microsoft SAL notation (sometime in 2014) to enable full static analysis and that is what i was hoping for.

See my comment here - https://news.ycombinator.com/item?id=50361418

However, i also understand the reasoning behind this C++26 contracts proposition - https://news.ycombinator.com/item?id=50364593

While i understand the rationale here, i can also see the problems it might lead to in the future. Correctness concerns should be an all-or-nothing proposition, you cannot do it piecemeal. Hence whether you want to call it "profiles" or not, i would like a Eiffel/Ada-SPARK like system which gives me complete static compile-time analysis and verification along with separate run-time guarantees.

The contracts committee should go back to the drawing board and come up with a more comprehensive solution.

This is a real chance to finally realize the "Safe C++" dream! Ada did it and so can C++.