Liste des Groupes | Revenir à cl c++ |
Paavo Helde writes:It's like people are like "I'm disciplined with
>On 03.01.2025 17:50, Sam wrote:>Paavo Helde writes:>>>
It might be obvious to you, but not for everybody. What exact
problem can be solved by validating thrown exception classes at
compile time?
Ummm… Making it logically impossible to throw an uncaught exception?
The code will refuse to compile because it will be ill-formed.
Getting rid of std::uncaught_exception(), completely?
I have never used std::uncaught_exception().
It's not typically used directly. Its primary user is your suffering C++
compiler, when it automatically injects a call to it in the code path
for uncaught exceptions.
>Well, I think I tried to>
use it once, 20 years ago, but it did not work in any useful way IIRC.
>
Anyway, avoiding uncaught exceptions is easy, one just has to place
catch(...) in main() and in all thread functions. Problem solved.
Sure, catch(...) deals with it. But it gives you nothing useful to work
with. A valiant attempt to send a bat-signal to std::current_exception
will eke out a few useful bits, if one's lucky, but the end result will
just be more spaghetti code.
>
If C++, the language, forced uncaught exception to be ill-formed then
this will pretty much negate the need for a wildcard catch, solving that
problem. I suppose that a wildcard catch can still be around, if someone
needed it. But it'll be viewed as an indication of lack of core C++
knowledge and experience.
>Double exceptions appearing during stack unwind are more tricky. Here>
some compiler support enforcing noexcept would be nice indeed. But
this would not need any more detailed exception specifications.
I think that double exceptions would naturally be minimized, in my ideal
C++ world. I think that exception-correctness would naturally eliminate
them. Exception correctness enforces strict exception handling, making
it more prominent.
>
Think about it. Destructors would be a hard noexcept. C++ then forces
you to write an explicit catch for any exceptions that get thrown from a
destructor. Mission accomplished.
>>>And>
how would a developer of a base class know which exceptions I might
want to throw from an overridden virtual function in a derived
class, which might be developed and compiled fully separately from
the base class?
The developer doesn't need to figure it out, the compiler will tell him.
You misunderstood my point. Yes, the compiler will refuse to compile
my code because a new algorithm implementation throws a new kind of
SocketException or whatever. So what next?
Fix the code, that's what's next.
>The goal is to compile my code, not to get>
compiler errors.
The only way to achieve the lofty goal is to fix the compiler errors.
So: fix them. This is no different than any other ABI break from a new
library version.
>Now I have to change the signatures of the umpteen functions in the>
call stack in umpteen classes in umpteen projects to let the new
required exception through (or translate it to something else via an
even more questionable hack).
This is the logically equivalent to a changed return type, a changed
parameter, or a type of any other object that's used commonly in the code.
>
I recall one time when I replaced a container that was used for
important stuff in my code. A whole bunch of functions returned this
container. The container used to be a std::list. It was now a
std::vector. I don't remember exactly how many functions referenced it.
Maybe 30-40, or so.
>
Now, I'm going to tell you something that will blow you away. Prepare to
be shocked. This revelation will rock your world. Make sure you're
sitting down. Seal all exits. Cancel the three-ring circus. Secure all
elephants in the zoo. Guess what?
>
I did not have to monotonically go through and update the 30-40 function
declarations.
>
Are you still with us, my friend? You survived such a shocking
reveleation?Good! Because, you see, C++ has this highly advanced,
space-age tool that only a select few C++ gurus know about.
>
It's called a "typedef". These days it's also known as a "using"
declaration.
>
All I had to do is change one typedef, and all those functions were now
returning a vector. After changing
>
typedef std::list<Widget> all_my_widgets;
>
to
>
typedef std::vector<Widget> all_my_widgets;
>
Then all the existing code, referencing "all_my_widgets", or
"all_my_widgets::iterator", or "all_my_widget_instance.begin()", and so
on – guess what? It's still compiled! OMIGOSH!!!!
>
I think I only needed to fix one or two things, that specifically
depended on the actual underlying implementation. The rest of the code,
that didn't care for the actual container, remained unchanged, and only
had to be recompiled.
>
So, in your case, your "umpteen functions" just need to be declared
correctly. Here's what happened on Alternate Earth, where exceptions
were actually implemented like that: the point you raised would've been
identified as a pain point early on, and something analogous to
"typedef", but for throw specifiers, was added to the language.
>
I dunno, maybe something like:
>
typedef throws(ClassA, ClassB) AlgoThrownClasses;
>
Then you go ahead and declare your algorithm:
>
void my_algorithm(algorithm_info_t &) throws(AlgoThrownClasses)
>
I'm now married to the actual syntax or grammar, that's just the syntax
that was used on Earth 2. Earth 3 used different syntax, and I don't
recall what it is, so I'll just focus on Earth 2's implementation. The
code you would write, on Earth 2, would use the alias:
>
void my_function_that_uses_algorithmm() throws(AlgoThrownClasses)
{
algorithm_info_t info;
>
my_algorithm(info);
}
>
Your Earth 2 doppelganger also threw its own exceptions:
>
void my_function_that_uses_algorithm() throws(AlgoThrownClasses,MyError)
>
All callers of my_function_that_uses_algorithm(), on Earth 2, used the
same alias.
>
* Pedantic note: your Earth 2 doppelganger will achieve greybeard status
by using, instead, "typedef throws(AlgoThrownClasses, MyError)
MyThrownClasses" and use that alias, instead.
>
Now, my friend, guess what happened if the algorithm was updated on
Earth 2 to throw a different exception:
>
typedef throws(ClassA, ClassB, ClassC) AlgoThrownClasses;
>
You'll be shocked and awed to learn that all those "umpteen functions"
will still compile just fine, on Earth 2.
>
The only additional work was done wherever in your code, on Earth 2, you
caught and handled the exceptions from the algorithms. And your
suffering C++ compiler, on Earth 2, will tell you exactly where the code
needs to be fixed, because it will now have an uncaught exception.
>
I wish I could move to Earth 2. But, on Earth 2, Donald Trump just won
his third term, and their border is already closed, and will be closed
for the foreseeable future.
>
Now, another thing just occured to me: what if the algorithm was changed
and it no longer throws one of the exceptions on Earth 2, and the
typedef alias was changed accordingly?
>
Well, in the existing code this results in a catch for an exception that
cannot possibly be thrown.
>
On Earth 2 it's an informational compiler diagnostic.
>
-Wall -Werror happily deals with it, on Earth 2.
>The point is that those umpteen functions could>
not care less about what exceptions go through them as they will just
pass them all through. So what's the point?
The point is that Earth 2 designed C++ properly, and on Earth 2 they
learned how to use C++ properly.
>At any frame in the call stack, if I want to catch a specific>
exception, I can do that. Everything else goes up to the top-level
catch(...) and gets logged or converted to an error message as
appropriate.
>
Why should I arbitrarily restrict what exceptions I can throw in some
function?
You can throw whatever you want, on Earth 2, you just have to declare
what you throw, and force yourself to catch everything you throw, and
your C++ compiler will force you to do the right thing. Resistance is
futile. You will be caught.
>
Les messages affichés proviennent d'usenet.