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

Why no structured bings in catch()?

71 views
Skip to first unread message

Mut...@dastardlyhq.com

unread,
Mar 8, 2023, 5:04:50 AM3/8/23
to
Just discovered that I can't do this in C++17:

throw pair<string,int>("hello",123)

catch(auto &[str,val])
{
}

Is there a good reason why this can't be implemented?

Öö Tiib

unread,
Mar 8, 2023, 6:24:00 AM3/8/23
to
Hmm I can't think of any way to implement it. Can you?

How can the compiler know what that exception could possibly be?
It has none of information at all. The throw can be anywhere far up
stack in dynamically linked library. So how to bind to unlimited kinds
of whatever (struct {? a, ? b;} , std::array<?,2>, std::tuple<?,?>,
std::pair<?, ?>, etc.) structurally by current language rules?

Mut...@dastardlyhq.com

unread,
Mar 8, 2023, 6:45:46 AM3/8/23
to
On Wed, 8 Mar 2023 03:23:52 -0800 (PST)
=?UTF-8?B?w5bDtiBUaWli?= <oot...@hot.ee> wrote:
>On Wednesday, 8 March 2023 at 12:04:50 UTC+2, Mut...@dastardlyhq.com wrote:
>> Just discovered that I can't do this in C++17:
>>
>> throw pair<string,int>("hello",123)
>>
>> catch(auto &[str,val])
>> {
>> }
>>
>> Is there a good reason why this can't be implemented?
>
>Hmm I can't think of any way to implement it. Can you?
>
>How can the compiler know what that exception could possibly be?
>It has none of information at all. The throw can be anywhere far up

Of course it has the information. It knows whats been thrown so it knows
if it can be captured in a structure binding. How does it know how to
set a binding from the return value of a function then?

>stack in dynamically linked library. So how to bind to unlimited kinds
>of whatever (struct {? a, ? b;} , std::array<?,2>, std::tuple<?,?>,
>std::pair<?, ?>, etc.) structurally by current language rules?

You could use the same argument for structured bindings anywhere.

Sam

unread,
Mar 8, 2023, 7:13:22 AM3/8/23
to
Mut...@dastardlyhq.com writes:

> >How can the compiler know what that exception could possibly be?
> >It has none of information at all. The throw can be anywhere far up
>
> Of course it has the information. It knows whats been thrown so it knows
> if it can be captured in a structure binding.

It only knows what's thrown at runtime. Not at compile time. It is
fundamental to C++: the types of all objects must be known at compile time.

"auto" doesn't change that. All that "auto" is, is an alias that says, in a
matter of speaking "unchanged", or "same as whatever you're assigning me".

When a function's return value is declared as "auto", the compiler looks at
the actual "return" statements, and uses whatever they do. If only the
declaration is available you'll skate by until either the function
definition is available, or until something tries to use its return value in
any way.

> How does it know how to
> set a binding from the return value of a function then?

Because it knows the actual type of the object that the function returns, as
long as its declared.

Try to do the same structural binding with a function that, itself, is
declared as returning an auto, without the function's body being compiled.
See how much progress you'll make.

> >stack in dynamically linked library. So how to bind to unlimited kinds
> >of whatever (struct {? a, ? b;} , std::array<?,2>, std::tuple<?,?>,
> >std::pair<?, ?>, etc.) structurally by current language rules?
>
> You could use the same argument for structured bindings anywhere.

No, you can't.

Mut...@dastardlyhq.com

unread,
Mar 8, 2023, 11:21:58 AM3/8/23
to
On Wed, 08 Mar 2023 07:13:05 -0500
Sam <s...@email-scan.com> wrote:
>Mut...@dastardlyhq.com writes:
>
>> >How can the compiler know what that exception could possibly be?
>> >It has none of information at all. The throw can be anywhere far up
>>
>> Of course it has the information. It knows whats been thrown so it knows
>> if it can be captured in a structure binding.
>
>It only knows what's thrown at runtime. Not at compile time. It is

It knows what is thrown at runtime.
It knows what *could* be thrown at compile time.

And if it knows what could be thrown and what catch statements are encompassing
those throws then it could compile in suitable types for an auto in the catch.

Bonita Montero

unread,
Mar 8, 2023, 12:27:19 PM3/8/23
to
With the thrown object there's some type information passed
along to verify if it fits against the type being catched.
If you want to catch sth. auto-ish you won't have a strong
type-match. So this doesn't work.

Bo Persson

unread,
Mar 8, 2023, 1:21:59 PM3/8/23
to
On 2023-03-08 at 17:21, Mut...@dastardlyhq.com wrote:
> On Wed, 08 Mar 2023 07:13:05 -0500
> Sam <s...@email-scan.com> wrote:
>> Mut...@dastardlyhq.com writes:
>>
>>>> How can the compiler know what that exception could possibly be?
>>>> It has none of information at all. The throw can be anywhere far up
>>>
>>> Of course it has the information. It knows whats been thrown so it knows
>>> if it can be captured in a structure binding.
>>
>> It only knows what's thrown at runtime. Not at compile time. It is
>
> It knows what is thrown at runtime.
> It knows what *could* be thrown at compile time.

But it doesn't. The called functions could be recompiled later, with new
exceptions added. Or a new version of a library could be installed.

How is the catch to know that?

>
> And if it knows what could be thrown and what catch statements are encompassing
> those throws then it could compile in suitable types for an auto in the catch.
>

Perhaps we need a templated catch that is instantiated at runtime? That
would be a novelty.

Andrey Tarasevich

unread,
Mar 8, 2023, 5:55:29 PM3/8/23
to
On 03/08/23 3:45 AM, Mut...@dastardlyhq.com wrote:
>>
>> How can the compiler know what that exception could possibly be?
>> It has none of information at all. The throw can be anywhere far up
>
> Of course it has the information. It knows whats been thrown so it knows
> if it can be captured in a structure binding.

No, it doesn't. Exception throwing and catching is a run-time mechanism
that cannot be fully analyzed at compile time in any reasonable sense of
the word. So, no, the compiler _does_ _not_ have that information.

> How does it know how to
> set a binding from the return value of a function then?

Easy. Because it fully knows the exact return type at compile time.

>> stack in dynamically linked library. So how to bind to unlimited kinds
>> of whatever (struct {? a, ? b;} , std::array<?,2>, std::tuple<?,?>,
>> std::pair<?, ?>, etc.) structurally by current language rules?
>
> You could use the same argument for structured bindings anywhere.

Nope. Structured bindings in C++ allowed only where the specific type of
the right-hand side is easily and immediately known at compile time.
Exceptions do not even remotely fit this description.

--
Best regards,
Andrey

Sam

unread,
Mar 8, 2023, 7:39:29 PM3/8/23
to
Mut...@dastardlyhq.com writes:

> On Wed, 08 Mar 2023 07:13:05 -0500
> Sam <s...@email-scan.com> wrote:
> >Mut...@dastardlyhq.com writes:
> >
> >> >How can the compiler know what that exception could possibly be?
> >> >It has none of information at all. The throw can be anywhere far up
> >>
> >> Of course it has the information. It knows whats been thrown so it knows
> >> if it can be captured in a structure binding.
> >
> >It only knows what's thrown at runtime. Not at compile time. It is
>
> It knows what is thrown at runtime.
> It knows what *could* be thrown at compile time.

No, it doesn't. If foo() calls bar() from another translation unit, there's
no information available to compiler to indicate what bar() could possibly
throw.

One of the biggest C++ design fuckups was the screwed up throw-specifiers,
that basically meant nothing useful, and they're now completely gone.

C++ should've handled throw specifiers the same way Java does, making it
possible to guarantee /at compile time/ that all exceptions get handled,

> And if it knows what could be thrown and what catch statements are
> encompassing
> those throws then it could compile in suitable types for an auto in the
> catch.

I'll write the bar(), you write the foo(), and we'll see if you know what
I'm throwing.


Mut...@dastardlyhq.com

unread,
Mar 9, 2023, 4:12:34 AM3/9/23
to
On Wed, 08 Mar 2023 19:39:11 -0500
Once something is thrown the runtime knows what it is and simply has to
match it with a catch as it does now. I'm not pretending matching an auto
would be simple but I don't see why it should be impossible. After all, the
runtime has to differentiate between int, char, parent and child classes to
do proper catching.

Mut...@dastardlyhq.com

unread,
Mar 9, 2023, 4:13:55 AM3/9/23
to
Does it need to be a strong match? If the runtime knows a pair has been
thrown - and it must or it couldn't be caught at all - then why can't it
do a match to see if an auto matches? Yes it'll be more complex than the
current system but I don't see why it would be impossible.

Bonita Montero

unread,
Mar 9, 2023, 4:15:05 AM3/9/23
to
Am 09.03.2023 um 10:12 schrieb Mut...@dastardlyhq.com:

> Once something is thrown the runtime knows what it is and simply has to
> match it with a catch as it does now. I'm not pretending matching an auto
> would be simple but I don't see why it should be impossible. ...

It is impossible since the code inside the catch-block could have
different unrelated types.


Öö Tiib

unread,
Mar 9, 2023, 6:18:10 AM3/9/23
to
Sam writes a function bar() that has switch case in it that throws
different objects from long list of struct {? a; ? b;} , std::array<?,2>, std::tuple<?,?>,
std::pair<?, ?>, etc. Each case different types in places of those
question marks. What do your str and val bind to in your foo() that calls
that bar()?



Paavo Helde

unread,
Mar 9, 2023, 7:06:48 AM3/9/23
to
09.03.2023 11:12 Mut...@dastardlyhq.com kirjutas:

>
> Once something is thrown the runtime knows what it is and simply has to
> match it with a catch as it does now. I'm not pretending matching an auto
> would be simple but I don't see why it should be impossible. After all, the
> runtime has to differentiate between int, char, parent and child classes to
> do proper catching.

Yes, and it generates code for handling them at compile time, for the
types which are explicitly specified in the catch clause.

If one wants to support catch handlers which catch any "templated" type,
one would need to generate catch handler code for all types present in
the program. This would need at least link-time code generation and
could easily produce catch handler code many times larger than the rest
of the program.

If the program uses dynamic libraries, link-time code generation would
not be sufficient. One would need to generate code at run time. This is
not something what is commonly done in C++. And what happens if it
appears the "template" cannot be instantiated for these new types? You
would get a compile error at run time. Ugh... that would not be C++ any
more.



Bonita Montero

unread,
Mar 9, 2023, 11:05:25 AM3/9/23
to
Am 09.03.2023 um 10:13 schrieb Mut...@dastardlyhq.com:

> Does it need to be a strong match? ...

With what you've shown it would be impossible to determine the types of
the bindings. And structured binding further wouldn't make sense because
no one wants to throw non-opaque types; all exceptions I use are opaque
and dont have directly acessible data members.

Daniel

unread,
Mar 9, 2023, 2:38:50 PM3/9/23
to
On Wednesday, March 8, 2023 at 5:04:50 AM UTC-5, Mut...@dastardlyhq.com wrote:

> Just discovered that I can't do this in C++17:
>
> throw pair<string,int>("hello",123)
>
> catch(auto &[str,val])
> {
> }
>

Others have answered the question about your catch statement. I would also
note that throwing a pair<string,int> isn't best practice, because a std:string
can throw during copying. We don't want to throw in an
exception copy constructor, lest we throw the wrong exception type.

You may want to consider inheriting from std::runtime_error, and store
the string part of your error message in it. std::runtime_error is guaranteed
not to throw exceptions during copying, typically it stores the string internally
as a separately-allocated reference-counted string.

Daniel

Christian Gollwitzer

unread,
Mar 10, 2023, 2:12:32 AM3/10/23
to
Am 09.03.23 um 10:13 schrieb Mut...@dastardlyhq.com:
I think you don't understand the problem. The problem is not matching
the pair with the auto in your catch phrase. The problem is how to
compile the code within the catch phrase. So, say, you do:


catch(auto &[str,val])
{
std::cout<<str.size();
}

What is the compiler supposed to do with the line that calls size() on
the object str? In regular code, the compiler knows, e.g. that str is of
type std::string. It then looks up the address of the function
std::string::size() and inserts a call to that address. But "str" could
be, despite its name, as well a std::vector. Then the call would need to
go to std::vector::size(), otherwise you get illegal memory thrashing.

The only way to implement something like that would be an OO system
where all the objects derive from a common base, like it is in Java,
and, at runtime, lookup the name of the member function in a jump table.
That means, OTOH, that the code in the catch would be compiled in a
completely different way than it is elswehere in C++, and run much
slower as well. Even a simple integer addition would need to go through
this kind of thing, because "a+b" could as well be string concatenation
(call to std::string::operator +) as well as a simple integer addition,
which compiles to a single machine instruction. This is the reason why
it can be implemented in, say, Python, where all code goes through an
interpreter loop, whereas in C++, it usually doesn't.


Christian

Mut...@dastardlyhq.com

unread,
Mar 10, 2023, 5:03:58 AM3/10/23
to
On Thu, 9 Mar 2023 11:38:42 -0800 (PST)
Daniel <daniel...@gmail.com> wrote:
>On Wednesday, March 8, 2023 at 5:04:50=E2=80=AFAM UTC-5, Mut...@dastardlyhq=
>..com wrote:
>
>> Just discovered that I can't do this in C++17:=20
>>=20
>> throw pair<string,int>("hello",123)=20
>>=20
>> catch(auto &[str,val])=20
>> {=20
>> }=20
>>=20
>
>Others have answered the question about your catch statement. I would also=
>=20
>note that throwing a pair<string,int> isn't best practice, because a std:st=
>ring
>can throw during copying. We don't want to throw in an
>exception copy constructor, lest we throw the wrong exception type.

If a there isn't enough memory to copy a short string then the program is going
to collapse in a heap fairly soon anyway so its not something that I'm going
to worry about.

>You may want to consider inheriting from std::runtime_error, and store=20
>the string part of your error message in it. std::runtime_error is guarante=
>ed
>not to throw exceptions during copying, typically it stores the string inte=
>rnally=20
>as a separately-allocated reference-counted string.=20

I'll bear it in mind.


Mut...@dastardlyhq.com

unread,
Mar 10, 2023, 5:05:20 AM3/10/23
to
Yes, I get the point. Perhaps this is why Apple chose Objective-C with its
rich runtime enviroment which can do reflection.

Sam

unread,
Mar 10, 2023, 6:58:57 AM3/10/23
to
Mut...@dastardlyhq.com writes:

> >I'll write the bar(), you write the foo(), and we'll see if you know what
> >I'm throwing.
>
> Once something is thrown the runtime knows what it is and simply has to
> match it with a catch as it does now. I'm not pretending matching an auto
> would be simple

catch (const auto &scooby_doo)
{
size_t mystery=sizeof(scooby_doo);
}

Can you tell me what my mystery is? The compiler has to compile it, somehow.
sizeof() is a compile-time constexpr.

> but I don't see why it should be impossible. After all, the
> runtime has to differentiate between int, char, parent and child classes to
> do proper catching.

Right, so the code has to be compiled here that might potentially catch an
int, char, long, double, every fundamental type, a pointer to every class in
existence, an array of one instance of every class in existence, an array of
two instances of every class in existence, and so in into infinity.

It will take a little bit of time for your sufferring C++ compiler to
generate code for all objects and classes that ever existed, or will ever
exist.

Öö Tiib

unread,
Mar 10, 2023, 8:05:00 AM3/10/23
to
On Friday, 10 March 2023 at 12:05:20 UTC+2, Mut...@dastardlyhq.com wrote:
>
> Yes, I get the point. Perhaps this is why Apple chose Objective-C with its
> rich runtime enviroment which can do reflection.

What you mean? There are no such feature about exceptions.
Objective-C throws only objects that have NSError interface.
Swift throws objects that have Error interface. Error and NSError are
exchangeable and interoperate but neither has some kind of generic
catches that can structurally bind to whatever is thrown.

Mut...@dastardlyhq.com

unread,
Mar 10, 2023, 11:14:59 AM3/10/23
to
Thanks for the heads up Mr Apple. Usually reflection means the runtime can
check types on the fly and do the exact thing I mentioned but I've never
used Obj-C so it was a guess.

0 new messages