Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

"How we used C++20 to eliminate an entire class of runtime bugs" by Cameron DaCamara

79 views
Skip to first unread message

Lynn McGuire

unread,
Jan 18, 2022, 8:24:37 PM1/18/22
to
"How we used C++20 to eliminate an entire class of runtime bugs" by
Cameron DaCamara

https://devblogs.microsoft.com/cppblog/how-we-used-cpp20-to-eliminate-an-entire-class-of-runtime-bugs/

"C++20 is here and has been supported in MSVC since 16.11, but today’s
post is not about how you can use it, but rather how we used it to
effectively eliminate an entire class of runtime bugs by hoisting a
check into compile-time. Let’s get right into it!"

Lynn

wij

unread,
Jan 20, 2022, 5:37:26 AM1/20/22
to
The idea of using errno for error-handling (mandatory) had developed nearly 20
years ago and very successful.
https://sourceforge.net/projects/cscall/files/latest/download
It is about error checking, or inline test (programmers don't like this term).
Coding syntax in this preliminary level may be too early (C++ is already burdensome
, mostly developed from enthusiasm, ideal and insufficient experiences. The result
might just be adding burdens, not 'feature', years latter).
Hope the language developer may realize the issue of error-handling is the
foundation of robust and efficient programs rather than syntax sugars.
(Exception is not suitable for error-handling)

Juha Nieminen

unread,
Jan 20, 2022, 6:43:27 AM1/20/22
to
wij <wyn...@gmail.com> wrote:
> The idea of using errno for error-handling (mandatory) had developed
> nearly 20 years ago and very successful.

'errno' alone may not be enough for error handling because sometimes you
want or need more details about the error.

Also, while not a show-stopper, 'errno' is easy to use wrong. Consider
how easy it is to do something like this:

if(!someFunction())
std::cerr << "Error calling someFunction(): " << std::strerror(errno);

What's wrong with that? The fact that it's at least theoretically possible
that that first operator<<() may change the value of errno (if it itself
encounters some kind of error), in which case it's going to print the
wrong error message.

(This might be the reason for those infamous and hilarious
"Error: No error" messages seen sometimes. It's not that there was no
error, but that 'errno' (or whatever the program uses) was changed
in between the actual error and showing the message.)

(Sure, the proper way to handle this is to take the value of errno into
a variable immediately after the function call, but consider how easy
it's to do mistakes like the one above.)

Mut...@dastardlyhq.com

unread,
Jan 20, 2022, 11:14:17 AM1/20/22
to
On Thu, 20 Jan 2022 11:43:11 -0000 (UTC)
Juha Nieminen <nos...@thanks.invalid> wrote:
>wij <wyn...@gmail.com> wrote:
>> The idea of using errno for error-handling (mandatory) had developed
>> nearly 20 years ago and very successful.
>
>'errno' alone may not be enough for error handling because sometimes you
>want or need more details about the error.
>
>Also, while not a show-stopper, 'errno' is easy to use wrong. Consider
>how easy it is to do something like this:
>
>if(!someFunction())
> std::cerr << "Error calling someFunction(): " << std::strerror(errno);
>
>What's wrong with that? The fact that it's at least theoretically possible
>that that first operator<<() may change the value of errno (if it itself
>encounters some kind of error), in which case it's going to print the
>wrong error message.
>
>(This might be the reason for those infamous and hilarious
>"Error: No error" messages seen sometimes. It's not that there was no
>error, but that 'errno' (or whatever the program uses) was changed
>in between the actual error and showing the message.)

AFAIK the standard C library only sets errno if an error occurs. It never
resets it to zero if there was no error. One would assume the C++ standard
libraries behave the same way so "Error: no error" would only occur if the
program has mistakenly seen a return value as a system error which would
set errno when it might actually be a higher level error which doesn't.

James Kuyper

unread,
Jan 20, 2022, 11:58:13 AM1/20/22
to
On 1/20/22 6:43 AM, Juha Nieminen wrote:
...
> (This might be the reason for those infamous and hilarious
> "Error: No error" messages seen sometimes. It's not that there was no
> error, but that 'errno' (or whatever the program uses) was changed
> in between the actual error and showing the message.)

When I've been in a position to check such things, I've usually found
that they are the result of calling perror() without first checking
whether errno was non-zero. When you use perror(), you should check to
make sure under which circumstances the previously called function sets
errno. Some things that are considered errors from the point of view of
the developer do not actually result in errno being set.

wij

unread,
Jan 20, 2022, 12:06:46 PM1/20/22
to
What programs need is a way to branch, errno is one basic way to do this.
I use class Errno that contains only one int data member.
class Errno {
int m_errno;
public:
constexpr Errno();
constexpr Errno(const Errno&);
...
};

My experience of using class Errno says errno (thread-local variable) could be
an option if such impl. could be faster(and smaller).

I did not recommend my implement but two points (debating such an issue might
be unnecessarily long and fruitless).
1. Make error checking mandatory
2. Use __FILE__, __LINE__ to mark WHERE error detected.

As I know, no programming language treats error-testing an essential part.
Most programmers don't like to use and see error-testing(or handling) codes for
each function call. But I found no way to escape, and my experience showed that
explicit testing for errors and handling it in the end is 'efficient', at least
for programmer's time (and thus money as well).
Others are implementation problems.

Ben Bacarisse

unread,
Jan 20, 2022, 12:15:07 PM1/20/22
to
wij <wyn...@gmail.com> writes:

> As I know, no programming language treats error-testing an essential
> part.

You might want to take a look at Icon. It makes extensive (and very
effective) use of the success and failure of even the most basic
operations forstructuring the code.

--
Ben.

James Kuyper

unread,
Jan 20, 2022, 12:23:02 PM1/20/22
to
On 1/20/22 11:14 AM, Mut...@dastardlyhq.com wrote:
> On Thu, 20 Jan 2022 11:43:11 -0000 (UTC)
> Juha Nieminen <nos...@thanks.invalid> wrote:
...
>> (This might be the reason for those infamous and hilarious
>> "Error: No error" messages seen sometimes. It's not that there was no
>> error, but that 'errno' (or whatever the program uses) was changed
>> in between the actual error and showing the message.)
>
> AFAIK the standard C library only sets errno if an error occurs.

Oddly enough, that's not the case:

"The value of errno in the initial thread is zero at program startup
(the initial value of errno in other threads is an indeterminate value),
but is never set to zero by any library function. 218) The value of
errno may be set to nonzero by a library function call whether or not
there is an error, provided the use of errno is not documented in the
description of the function in this document." (C standard 7.5p3).

Implicit in that statement is the fact that "provided the use of errno
is ... documented in the description of the function in this document.",
then that function may only use errno as specified by that documentation.

> ... It never
> resets it to zero if there was no error.

As specified above, it never sets it to zero at all; that occurs only at
program startup, and only for the initial thread.

> ... One would assume the C++ standard
> libraries behave the same way so "Error: no error" would only occur if the
> program has mistakenly seen a return value as a system error which would
> set errno when it might actually be a higher level error which doesn't.

The C++ standard prohibits the routines specified in <system_error>,
from changing the value of errno (19.5p2). Otherwise, it never says
anything to either mandate or prohibit changing errno. I would hope that
what the C standard says about the use of errno by the C standard
library can, by extension, be applied to the rest of the C++ standard
library, but there's nothing that says so explicitly.

Andrey Tarasevich

unread,
Jan 20, 2022, 12:36:03 PM1/20/22
to
What it basically boils down to is that C++20's `consteval` triggers a
hard error for non-compile-time evaluations, as opposed to `constexpr`,
which silently opted for run-time evaluation.

--
Best regards,
Andrey Tarasevich

Ian Collins

unread,
Jan 20, 2022, 2:50:24 PM1/20/22
to
So it's better to pick up the error at compile time, isn't it?

The gcc and clang compilers have something similar for printf like
logging with __attribute__ ((format (printf, FORMAT_ARG, VARG_BEGIN)))
which has most likely saved many a run time error in similar situations.

--
Ian.

Richard

unread,
Jan 20, 2022, 5:19:30 PM1/20/22
to
[Please do not mail me a copy of your followup]

Lynn McGuire <lynnmc...@gmail.com> spake the secret code
<ss7p85$p00$2...@dont-email.me> thusly:

>"How we used C++20 to eliminate an entire class of runtime bugs" by
>Cameron DaCamara
>
>https://devblogs.microsoft.com/cppblog/how-we-used-cpp20-to-eliminate-an-entire-class-of-runtime-bugs/

Nice success story!
--
"The Direct3D Graphics Pipeline" free book <http://tinyurl.com/d3d-pipeline>
The Terminals Wiki <http://terminals-wiki.org>
The Computer Graphics Museum <http://computergraphicsmuseum.org>
Legalize Adulthood! (my blog) <http://legalizeadulthood.wordpress.com>

Bonita Montero

unread,
Jan 21, 2022, 4:00:54 AM1/21/22
to
Am 20.01.2022 um 18:35 schrieb Andrey Tarasevich:


> What it basically boils down to is that C++20's `consteval` triggers a
> hard error for non-compile-time evaluations, as opposed to `constexpr`,
> which silently opted for run-time evaluation.

"if constexpr( )" is always compile-time evaluated.

Juha Nieminen

unread,
Jan 21, 2022, 4:03:18 AM1/21/22
to
'if constexpr' is not really a constexpr construct. It simply reuses the
keyword.

Bonita Montero

unread,
Jan 21, 2022, 6:51:32 AM1/21/22
to
> 'if constexpr' is not really a constexpr construct. ...

Of course, the expression has to be compile-time evaluated.

Andrey Tarasevich

unread,
Jan 21, 2022, 1:19:28 PM1/21/22
to
`if constexpr` can be used to test compile-time nature of an expression,
but it can only be done "from the outside" of that expression. I don't
immediately see how would one go about using `if constexpr` for function
argument verification, especially considering that that verification has
to be injected after template argument deduction.

In order to achieve that injection they used a well-known technique,
which is as old as the world itself. They implemented an intermediate
pass-through `Checker` class, which performs the necessary checks in its
constructor. Nothing new here.

However, previously we would only be able generate run-time errors
(assertion failures) from inside that constructor. That's how this
technique has been used for ages.

They show that now, by declaring that constructor as `consteval`, one
can also generate compile-time errors (!) from inside that constructor.

Of course, by doing so one's restricting oneself to compile-time
arguments exclusively. But apparently in their application they don't
mind that. And, if I'm mot mistaken, one can get the best of both worlds
by branching on `if (std::is_constant_evaluated)`/`if consteval`.

Bonita Montero

unread,
Jan 21, 2022, 1:51:42 PM1/21/22
to
You can virtually even check parameters with it:

template<bool B>
void f( bool_constabt<B> x )
{
if constexpr( B )
...
}

I use it very often for templated parameters, sometimes
to check their type-properties, f.e. if they're integral.

Andrey Tarasevich

unread,
Jan 21, 2022, 3:03:55 PM1/21/22
to
You are checking a template argument - an entity that is inherently
compile-time. "There is no renown in that." Many techniques are
available for checking template arguments at compile time even in C++98.

Meanwhile, they want to check regular function arguments (!) at compile
time (under assumption that they are supplied with compile-time
arguments, of course). That is a wholly different story.

And they also want to be able to check template arguments based on such
regular function arguments.

E.g.

#include <string_view>
#include <type_traits>

template <typename T>
struct Checker {
consteval Checker(const char* fmt)
{
if (fmt == std::string_view{ "int" } &&
std::is_same_v<T, int>)
return;

if (fmt == std::string_view{ "double" } &&
std::is_same_v<T, double>)
return;

throw;
}
};

template <typename T>
void fmt(std::type_identity_t<Checker<T>> checked, T) {}

int main()
{
fmt("int", 42); // OK
fmt("double", 1.23); // OK
fmt("int", "foo"); // Compile error
0 new messages