Exceptions is this ancient weird and scary topic that we could gladly hear some best practices on. Thanks Jason for bringing this up, I look forward to hear more.
Very insightful topic.. I have a project where exceptions are thrown everywhere, at the time I developed just ignored them all. This video reminds me I need to revisit them.
I always learn something from Jason's videos (even though I've been at this for longer than most viewers have been extant...): I wasn't previously aware that structured exception handling went in order of most-derived class first.
I've never really made use of exceptions, and at my current job, which I've had for 9 years, we don't use em at all, partly because we compile (parts of the codebase) for some platforms that don't support them, partly just legacy decision. Def interested in best practices wrt to exceptions, I've never seen good examples afaik. nor have I ever really invested in learning about them.
Along with the other calls for your thoughts on best practices with exceptions, I also really want to hear what you think about nested exceptions! I currently use them to collect select pieces of state from across the call stack. I find this idea very valuable, but maybe it's not as good an option as I think it is.
Exceptions have their purpose but knowing when, where and how to use them is key. If one automatically thinks that exceptions are bad, useless, ill-structured, etc... then for me, it's more of a skill issue. Like any other feature of a given language, exceptions are a tool within the tool box. Each tool has a generalized purpose, but with any and every tool, there are proper and improper ways to use them. For example, you're not going to use a circular saw with a blade designed to cut plywood to cut through concrete. If one doesn't know their tools and then complains that the tool is bad... again, it's a skill issue!
I see. So C++ is a gigantic and massively complex language that I don't believe anyone knows all the features of or how these feature interact with each other and it's the users fault, their "skill issue", that they struggle with it and make mistakes. Blame the dumb users.
@@Heater-v1.0.0 Poorly written programs are designed by inadequacy. What do I mean by this? It's the inadequacy of knowing how to build a proper foundation. This isn't just tied to programming, this is prevalent in all industries. Do a bit of research on the study, science and engineering involved with the construction of building foundations. Different methods are used in different regions. If one doesn't understand and know that they are building on clay instead of bedrock and they use the same materials or method for both, they will soon learn and realize that one of their buildings isn't going to stand for very long. Make a building with a poor foundation. How long is that building going to stand? When it does eventually collapse whose fault was it? Was it the material's fault? Was it the ground's fault? Was it the sky's fault? No! It was the fault of those who built it not fully knowing how to build a proper foundation within a given environment. Consider this: A blacksmith doesn't become a master or journeyman without first being an apprentice or novice. And they don't improve their skills without first failing a few times. These types of constraints are why we have both conventions and standards. It's up to the craftsman to know their craft. Within programming this is why we have debuggers, profilers, analyzers, etc... and why we need to unit test, and constantly check for edge cases. It's a matter one's ability to learn and know the tool they are using. It has nothing to do with them being smart or dumb. Very rarely does one make a perfect program the very first time. Sure there are some exceptions but even then those who are born with a natural talent over others who have to learn the craft are bound to eventually make mistakes at some point in time, somewhere. This is what makes us human. One's capability to know their set of tools and how and when to use them is 100% a skill issue. It always has been even outside of programming. It's no different for a carpenter, a blacksmith, a gardener, a painter, a musician, a surgeon, etc... It's always a skill issue!
@@skilz8098 I agree. We want hightly trained, practiced, skilled, tallented, imaginative people be build large complex software systems. Systems that need to be robust, reliable, performant, usable etc. To that I would add that we need such people to be passionate, honest, methodical, etc, etc. That really has nothing to do with the language chosen. Although of course many languages may make the job easier and/or contribute to the desired out comes. While you are on the anologies with real architecture and building construction I would like to point out that the huge, complex, towering, labyrinth that is C++ is itself built on the foundations of the rickety garden shed that is C. People should not have to waste their time learning all the complexity of C++ and especially all the pitfalls that can cause their buildings to collapse without warning at any moment. A programming language should help guard against ones mistakes not make it easier to make mistakes and hide flaws.
@@Heater-v1.0.0 I disagree. Why? Even though C++ is built from the basics or foundations of C it is NOT C. And besides that, C is still a prevalent language especially in embedded systems and operating systems programming. C itself is the very powerful and close to the metal. C++ shares the same concept in that it is designed to be fast and efficient, portable and reliable. It has the philosophy of you only pay for what you use with very minimal to almost no overhead. I've never had any formal education or training in computer science or software engineering. When I started to get into programming in my early 20s in the early 2000s I started off with C/C++ and a little bit of C# primarily for learning how to build a working 3D Graphics/Game Engine from scratch. I didn't use a pre built game engine such as Unreal or Unity. I didn't use any kind of prebuilt libraries such as SML, etc... I learned all the inner workings under the hood using both DirectX 9.0c - 10 and OpenGL 1.0 before moving on to modern OpenGL. I written my own math / vector / matrix library. I written my own camera and lighting classes. I written my own Scene Graph Hierarchy along with an robust file parser to load modified game objects at runtime as opposed to having to recompile and rebuild the entire project by changing the size, position, orientation, color of an object. I even written my own text rendering class with a built in console with a small scripting language (another compiler) to dynamically change GUI elements. The entire GUI system was written. The entire project consisted of over 100,000 lines of C/C++ code. I even undergone looking for bottlenecks and including inline ASM instructions to unroll specific loops to improve efficiency and cache coherency and to minimize the branch predictor. Both C and C++ are very powerful languages. They're not going to go away anytime soon. Almost every Operating System you use: Linux, Windows, Android, Mac, etc... is written in C or C++. Almost every spread sheet or office program one uses again written in C/C++. Just about every Adobe suite application again written in C/C++. Just about any Image, Video or Audio Editing Software again is probably written in C/C++. Just about every Web Browser is again written in C/C++. Even most of your Compilers, Interpreters, Linkers and Debuggers again are written in more than likely in C/C++. These two languages are the next closest thing to the actual hardware one step above assembly. This is what makes them so powerful. Also the culture of the implementation and design of the languages is that they do not force the programmer into specific and strict dogmatic overly pedantic paradigms. They are more robust and allow the programmer choice of actions. Sure there is a learning curve to them but that's a part of the package of having a multipurpose tool. Don't get me wrong, interpreted languages do have their uses. They are still viable tools for specific domains. Yet, C/C++ will still be around for another 50+ years! As long as computers remain in the Binary Digit (Digital) domain with an accompanied ISA - Assembly Language interface, C and C++ will not be going anywheres. If one is doing basic Front End Web Development then sure Java/JavaScript even Python might suffice. If one is doing the middle or front end server - database then sure SQL would be appropriate. Now if they are doing back end boilerplate code, that's where C/C++ shines. Their compilers and runtime environments are tightly integrated with the heart of the operating systems' kernels. It depends on the job at hand at which tool ought to be used. Another consideration is if you are working for some company who invested thousands even millions into said technologies, libraries, etc... then you might be forced into having use what they are providing you with. What makes a person so versatile isn't so much the ability to use or know different or specific languages, nor even their ability to debug or to even troubleshoot issues, but the ability to actual craft their own tools! Again, it's always a skill issue! Even one's ability to research and to learn is also a skill issue!
@@skilz8098 I can only agree with most of what you have said there. But I have to comment: Of course C++ is not C. The C language is small, simple, elegant, performant programming language that pretty much perfectly fits the problem space it was created to fill, that is enabling the rewriting of Unix in a high-level, portable language thus making Unix more widely available. That of course made C an excellent systems programming language for all manner of other jobs on all manner of platforms. On the other hand C++ is a huge complex labyrinth of chaos that nobody understands all the features of or how they interact with each other. However C++ is in fact, to a large extent an extension of C. Recall that C++ started out as a preprocessor to C. Yes C++ added a lot of nice higher level language features, but at the same time it clung to all the worst features of C and still does. Which makes it an ugly, dangerous monster. It's growing all the time without ever fixing the basic problems. Personally I wish C++ had removed and/or fixed up as many of the problems with C as it could a long tome ago. Yes that would have made it not compile old C code, but so what, that old C code was just an extern "C" away. We could have had a far more elegant and robust high level language derived from C instead of the mutant we have today. I don't know why you have brought up interpreted languages. For the record I stated programming in 1973 being taught BASIC and assembler on a mainframe. Later I found myself programming the new fangled microprocessors in assembler, even writing code in hexadecimal as we had no assembler at the time. I moved on through ALGOL, Coral, PL/M, Lucol, C, Pascal, C++. My only serious experience of interpreted languages was a couple of dark years when I had to scratch a living in PHP and much later Python and Javascript. I agree, skill is an issue, sill is always desirable. However part of a craftsman's skill is realising when that old tool one has been using for years is so broken, with so many unreliable fixes bodged on to it that it actually better to invest a new tool than to try and constantly overcome the problems of the old one. .
i use exceptions when i can because i like RAII and usable return values. i dislike output parameters, error codes and checking the error codes. i want to write code that is not constantly checking whether something went ok or not (it's ugly). the normal case path goes past all of that most of the time.
RAII is one of the primary reasons this mindset doesn't work in a lot of situations (not gone over here). Unfortunately sometimes C++ just doesn't give you the tools to not make your tools ugly, or forces it to be ugly to do the "right" thing, there's no rust ? in C++, and you need tl::expected a long with monadic optional for proper error handling, along with `has_value()`. It sucks to check expected for errors constantly, but if you're a library author, you very likely shouldn't be using exceptions as your first line of defense in error handling, because now you force that decision on *everyone else*. Do what ever you want in your code, but don't force bad error handling decisions on *your users* (like stdio, at(i) or constructors that throw exceptions) because *you* were too lazy to work around C++'s massive limitations. As library developers you have to write a lot of boiler plate anyway (dllimport etc...), making sure people have the tools they need to use your library is just part of being a diligent library designer.
@@snbv5real I remember hearing / reading somewhere that internally Microsoft throws exceptions and then converts them into HRESULTS, and this is mostly due to ABI issues as in needing to compile with the same settings and compiler as Windows itself. To be honest, I wouldn't mind being able to just directly work with those exceptions instead of dealing with HRESULTS, but this is basically to say, unless you have some specific constraints, why care what error handling method a library is using?
Not having to babysit error codes all the way up a call chain is huge. Also huge is the fact that you cannot drop an exception on the floor by error of omission. Working in a codebase right now where there are dozens of instances of return codes being ignored and it is very worrying to say the least.
I would love to see more videos on exceptions, especially in an embedded context. They are one of the things that is just agreed shouldn’t be used. But I feel that in most embedded contexts that aren’t handling life or death situations exceptions could be really useful. Typical error handling in these context need to happen in centralised manner because recovering from were the error took place is often not possible making exceptions a way to reduce return error codes up 6 functions deep and reduce if checks in after every return.
I would love to hear your thoughts on why you think `catch(...)` is a code smell. I've been on and off experimenting with exceptions for the past 4 years and one of the things I've wondered was whether or not one should care about the type of the thrown exception. Recently I've come to conclusion that in most use cases, I don't think we need to care about the exact exception. I actually think most functions that do catch will have a reasonable default action that can occur, think like `std::optional::value_or()` or `std::optional::or_else()`. My logic being here is that when you catch something, this should not affect the overall system, and by that I mean catch handlers up the stack should not care if a new catch handler was introduced down the stack. This should also just apply in general to functions, a function either succeeded or failed, if a function internally fails but catches the error and turning it into a succeeded case, callers should be unaffected by this. I mostly look at this in the eyes of if you're going to introduce a try / catch, it should encompass the whole function, and if it doesn't, move it to a new function. This is mostly because I think error handling quickly increases the complexity of a function. To me, there is just less mental overhead of having a function either just be transparent to errors (no errors can occur, or would only like to propagate the error), or is one that can actually handle the error.
In my experience, if you're using exceptions, you often want different types of exceptions for different classes of errors, so you can handle/report them in different ways. So I would say a catch(...) is a smell because you don't have any way of knowing/logging what kind of error occured.
@@cppweekly Hmm, I consider logging as part of the clean up process, so it's better served being hidden through a scope guard or a generic function. Something like auto try_log_failure(auto callable) { try { return callable(); } catch(const exception& e) { log_and_rethrow(e); } catch(...) { log_and_rethrow("unexpected failure"); } } This way, in most cases, they don't particularly care about the type of exception being thrown reducing special cases. This would also increase the amount of swallowing exceptions if the whole purpose of the catch was to just log to consider it handled as they would now just be replaced with empty catches. Swallowing exceptions is also something I don't think isn't that bad if done with intent, which is hard to gauge. I'm interested to see a code base that would regularly catch a specific type as it subverts my expectation of it being rare among the already rare amount of catch statements a program would have.
Exceptions are actually really competitive in some scenarios. For instance, imagine if we had to check for an error code or something every time we called std::print. It would be a mess (too many if statements). Instead it uses exceptions so we only need to have a single try catch block for as many calls to std::print as we want.
I think this also shows a lack of good syntax to quickly handle errors as value. This is why the "?" operator in rust is nice, and i think it can (in part) solve this problem
@@Stowy In general, expected and exceptions are 2 sides of the same coin with various language support to make one or the other nicer to use. In C++ specifically as of 2024, expected excels in transforming a single value in a more readable way with their monad functions. You could technically do the same right now for exceptions, but it'd be using the inside out syntax of doing `c(b(a()));`. If we had any form of UFCS / extension methods, exceptions could also do `a().b().c()` that is currently achieved via monads with expected. Like you said rust having operator ? to do a implicit `if(failed) return;` is great while exceptions in general, is implicit. That being said, if we did reach a feature parity that can make both expected and exceptions be equally as ergonomic to use, I would still use exceptions merely from disliking the need to unwrap boxed values.
the good thing about exception is that you don't have to manage the passthrough of your dependencies errors. you don't even care what those errors are, you simply have to catch those that concerns you, and leave the rest be for others to manage.
The one place where exceptions are indispensable is in constructors, which otherwise have no way of returning error conditions, at least if you're taking RAII remotely seriously. Otherwise they are one of many options for error reporting. In my view, throwing any exception type that is not a class derived from std::exception falls into the "you'd better have a REALLY good reason for this" category.
If making the object can throw, I prefer to use something like the "named constructor" pattern. The real constructor just moves its parameters into fields and otherwise does nothing. It can be private. Then public static methods actually do the work of constructing the data and passing it into the private constructor. Errors can be handled however one wants, error code, exceptions, std::optional, std::expected, or whatever.
I have two gripes with exceptions: 1. They normally make use of the heap (well, in all implementations I know of). Which means that libraries which use exceptions are grouped into "useable in environments where you can't or aren't allowed to use a heap" and "not useable in environments where you can't or aren't allowed to use a heap". 2. Some implementors or designers of certain libraries (looking at you, fstream classes) thought that it would be a good idea to use them for reporting errors which can be fairly common. Seriously, don't use exceptions for possibly common errors.
I haven't seen jet a good C++ library/framework that uses exceptions good, I have seen Python modules that uses exceptions horribly, however in Php the framework Laravel uses Exceptions so well I doubt there is a better practical examples of how to use them.
@@KhalilEstell Simple stuff for example the functions you create run inside a try/catch space you don't need to create, and several functions may throw exceptions, and depending on the exception it redirect to something different (Laravel is web after all). A more concrete example if you call findOrFail() and can't find, it throws the exception and the part that catches it redirect to a 404. Another is for example you can define policies function and call gates; if a gate is call, it check the policy associated, if the policies returns false, the gate throws an exception and it returns a 403. So you only think about the app logic assuming everything is working and if something goes wrong it gets auto-handled.
Never heard or thought of the Lippincott handler, extremely useful!
Exceptions is this ancient weird and scary topic that we could gladly hear some best practices on. Thanks Jason for bringing this up, I look forward to hear more.
best practice: disable exceptions.
Thank you very much for this! Would love more (not too complex) talks on the subject and other "basics" from your experience POV
Very insightful topic.. I have a project where exceptions are thrown everywhere, at the time I developed just ignored them all. This video reminds me I need to revisit them.
This video was Exceptional!
Missed opportunity in the outro - should've been "I will catch you in the next exception."
no
He can try
I always learn something from Jason's videos (even though I've been at this for longer than most viewers have been extant...): I wasn't previously aware that structured exception handling went in order of most-derived class first.
I've never really made use of exceptions, and at my current job, which I've had for 9 years, we don't use em at all, partly because we compile (parts of the codebase) for some platforms that don't support them, partly just legacy decision.
Def interested in best practices wrt to exceptions, I've never seen good examples afaik. nor have I ever really invested in learning about them.
Along with the other calls for your thoughts on best practices with exceptions, I also really want to hear what you think about nested exceptions!
I currently use them to collect select pieces of state from across the call stack. I find this idea very valuable, but maybe it's not as good an option as I think it is.
Can you post an episode request here with more details and an example? github.com/lefticus/cpp_weekly/issues
Exceptions have their purpose but knowing when, where and how to use them is key. If one automatically thinks that exceptions are bad, useless, ill-structured, etc... then for me, it's more of a skill issue. Like any other feature of a given language, exceptions are a tool within the tool box. Each tool has a generalized purpose, but with any and every tool, there are proper and improper ways to use them. For example, you're not going to use a circular saw with a blade designed to cut plywood to cut through concrete. If one doesn't know their tools and then complains that the tool is bad... again, it's a skill issue!
I see. So C++ is a gigantic and massively complex language that I don't believe anyone knows all the features of or how these feature interact with each other and it's the users fault, their "skill issue", that they struggle with it and make mistakes. Blame the dumb users.
@@Heater-v1.0.0 Poorly written programs are designed by inadequacy. What do I mean by this? It's the inadequacy of knowing how to build a proper foundation. This isn't just tied to programming, this is prevalent in all industries.
Do a bit of research on the study, science and engineering involved with the construction of building foundations. Different methods are used in different regions. If one doesn't understand and know that they are building on clay instead of bedrock and they use the same materials or method for both, they will soon learn and realize that one of their buildings isn't going to stand for very long.
Make a building with a poor foundation. How long is that building going to stand? When it does eventually collapse whose fault was it? Was it the material's fault? Was it the ground's fault? Was it the sky's fault? No! It was the fault of those who built it not fully knowing how to build a proper foundation within a given environment.
Consider this: A blacksmith doesn't become a master or journeyman without first being an apprentice or novice. And they don't improve their skills without first failing a few times.
These types of constraints are why we have both conventions and standards. It's up to the craftsman to know their craft. Within programming this is why we have debuggers, profilers, analyzers, etc... and why we need to unit test, and constantly check for edge cases.
It's a matter one's ability to learn and know the tool they are using. It has nothing to do with them being smart or dumb. Very rarely does one make a perfect program the very first time. Sure there are some exceptions but even then those who are born with a natural talent over others who have to learn the craft are bound to eventually make mistakes at some point in time, somewhere. This is what makes us human.
One's capability to know their set of tools and how and when to use them is 100% a skill issue. It always has been even outside of programming. It's no different for a carpenter, a blacksmith, a gardener, a painter, a musician, a surgeon, etc... It's always a skill issue!
@@skilz8098 I agree. We want hightly trained, practiced, skilled, tallented, imaginative people be build large complex software systems. Systems that need to be robust, reliable, performant, usable etc. To that I would add that we need such people to be passionate, honest, methodical, etc, etc.
That really has nothing to do with the language chosen. Although of course many languages may make the job easier and/or contribute to the desired out comes.
While you are on the anologies with real architecture and building construction I would like to point out that the huge, complex, towering, labyrinth that is C++ is itself built on the foundations of the rickety garden shed that is C.
People should not have to waste their time learning all the complexity of C++ and especially all the pitfalls that can cause their buildings to collapse without warning at any moment. A programming language should help guard against ones mistakes not make it easier to make mistakes and hide flaws.
@@Heater-v1.0.0 I disagree. Why? Even though C++ is built from the basics or foundations of C it is NOT C. And besides that, C is still a prevalent language especially in embedded systems and operating systems programming. C itself is the very powerful and close to the metal. C++ shares the same concept in that it is designed to be fast and efficient, portable and reliable. It has the philosophy of you only pay for what you use with very minimal to almost no overhead.
I've never had any formal education or training in computer science or software engineering. When I started to get into programming in my early 20s in the early 2000s I started off with C/C++ and a little bit of C# primarily for learning how to build a working 3D Graphics/Game Engine from scratch. I didn't use a pre built game engine such as Unreal or Unity. I didn't use any kind of prebuilt libraries such as SML, etc... I learned all the inner workings under the hood using both DirectX 9.0c - 10 and OpenGL 1.0 before moving on to modern OpenGL.
I written my own math / vector / matrix library. I written my own camera and lighting classes. I written my own Scene Graph Hierarchy along with an robust file parser to load modified game objects at runtime as opposed to having to recompile and rebuild the entire project by changing the size, position, orientation, color of an object.
I even written my own text rendering class with a built in console with a small scripting language (another compiler) to dynamically change GUI elements. The entire GUI system was written.
The entire project consisted of over 100,000 lines of C/C++ code. I even undergone looking for bottlenecks and including inline ASM instructions to unroll specific loops to improve efficiency and cache coherency and to minimize the branch predictor.
Both C and C++ are very powerful languages. They're not going to go away anytime soon.
Almost every Operating System you use: Linux, Windows, Android, Mac, etc... is written in C or C++. Almost every spread sheet or office program one uses again written in C/C++. Just about every Adobe suite application again written in C/C++. Just about any Image, Video or Audio Editing Software again is probably written in C/C++. Just about every Web Browser is again written in C/C++. Even most of your Compilers, Interpreters, Linkers and Debuggers again are written in more than likely in C/C++.
These two languages are the next closest thing to the actual hardware one step above assembly. This is what makes them so powerful. Also the culture of the implementation and design of the languages is that they do not force the programmer into specific and strict dogmatic overly pedantic paradigms. They are more robust and allow the programmer choice of actions. Sure there is a learning curve to them but that's a part of the package of having a multipurpose tool.
Don't get me wrong, interpreted languages do have their uses. They are still viable tools for specific domains. Yet, C/C++ will still be around for another 50+ years! As long as computers remain in the Binary Digit (Digital) domain with an accompanied ISA - Assembly Language interface, C and C++ will not be going anywheres.
If one is doing basic Front End Web Development then sure Java/JavaScript even Python might suffice. If one is doing the middle or front end server - database then sure SQL would be appropriate. Now if they are doing back end boilerplate code, that's where C/C++ shines. Their compilers and runtime environments are tightly integrated with the heart of the operating systems' kernels.
It depends on the job at hand at which tool ought to be used. Another consideration is if you are working for some company who invested thousands even millions into said technologies, libraries, etc... then you might be forced into having use what they are providing you with.
What makes a person so versatile isn't so much the ability to use or know different or specific languages, nor even their ability to debug or to even troubleshoot issues, but the ability to actual craft their own tools!
Again, it's always a skill issue! Even one's ability to research and to learn is also a skill issue!
@@skilz8098 I can only agree with most of what you have said there. But I have to comment:
Of course C++ is not C. The C language is small, simple, elegant, performant programming language that pretty much perfectly fits the problem space it was created to fill, that is enabling the rewriting of Unix in a high-level, portable language thus making Unix more widely available. That of course made C an excellent systems programming language for all manner of other jobs on all manner of platforms. On the other hand C++ is a huge complex labyrinth of chaos that nobody understands all the features of or how they interact with each other. However C++ is in fact, to a large extent an extension of C. Recall that C++ started out as a preprocessor to C. Yes C++ added a lot of nice higher level language features, but at the same time it clung to all the worst features of C and still does. Which makes it an ugly, dangerous monster. It's growing all the time without ever fixing the basic problems.
Personally I wish C++ had removed and/or fixed up as many of the problems with C as it could a long tome ago. Yes that would have made it not compile old C code, but so what, that old C code was just an extern "C" away. We could have had a far more elegant and robust high level language derived from C instead of the mutant we have today.
I don't know why you have brought up interpreted languages. For the record I stated programming in 1973 being taught BASIC and assembler on a mainframe. Later I found myself programming the new fangled microprocessors in assembler, even writing code in hexadecimal as we had no assembler at the time. I moved on through ALGOL, Coral, PL/M, Lucol, C, Pascal, C++. My only serious experience of interpreted languages was a couple of dark years when I had to scratch a living in PHP and much later Python and Javascript.
I agree, skill is an issue, sill is always desirable. However part of a craftsman's skill is realising when that old tool one has been using for years is so broken, with so many unreliable fixes bodged on to it that it actually better to invest a new tool than to try and constantly overcome the problems of the old one.
.
i use exceptions when i can because i like RAII and usable return values. i dislike output parameters, error codes and checking the error codes. i want to write code that is not constantly checking whether something went ok or not (it's ugly). the normal case path goes past all of that most of the time.
I totally agree with this sentiment. Keep an eye out for some future papers.
RAII is one of the primary reasons this mindset doesn't work in a lot of situations (not gone over here). Unfortunately sometimes C++ just doesn't give you the tools to not make your tools ugly, or forces it to be ugly to do the "right" thing, there's no rust ? in C++, and you need tl::expected a long with monadic optional for proper error handling, along with `has_value()`. It sucks to check expected for errors constantly, but if you're a library author, you very likely shouldn't be using exceptions as your first line of defense in error handling, because now you force that decision on *everyone else*. Do what ever you want in your code, but don't force bad error handling decisions on *your users* (like stdio, at(i) or constructors that throw exceptions) because *you* were too lazy to work around C++'s massive limitations. As library developers you have to write a lot of boiler plate anyway (dllimport etc...), making sure people have the tools they need to use your library is just part of being a diligent library designer.
@@snbv5real I remember hearing / reading somewhere that internally Microsoft throws exceptions and then converts them into HRESULTS, and this is mostly due to ABI issues as in needing to compile with the same settings and compiler as Windows itself.
To be honest, I wouldn't mind being able to just directly work with those exceptions instead of dealing with HRESULTS, but this is basically to say, unless you have some specific constraints, why care what error handling method a library is using?
Not having to babysit error codes all the way up a call chain is huge. Also huge is the fact that you cannot drop an exception on the floor by error of omission. Working in a codebase right now where there are dozens of instances of return codes being ignored and it is very worrying to say the least.
I would love to see more videos on exceptions, especially in an embedded context. They are one of the things that is just agreed shouldn’t be used. But I feel that in most embedded contexts that aren’t handling life or death situations exceptions could be really useful.
Typical error handling in these context need to happen in centralised manner because recovering from were the error took place is often not possible making exceptions a way to reduce return error codes up 6 functions deep and reduce if checks in after every return.
Do keep your eyes open, I know that we are going to see content soon from someone discussing specifically exceptions in embedded environments.
I would love to hear your thoughts on why you think `catch(...)` is a code smell. I've been on and off experimenting with exceptions for the past 4 years and one of the things I've wondered was whether or not one should care about the type of the thrown exception. Recently I've come to conclusion that in most use cases, I don't think we need to care about the exact exception. I actually think most functions that do catch will have a reasonable default action that can occur, think like `std::optional::value_or()` or `std::optional::or_else()`.
My logic being here is that when you catch something, this should not affect the overall system, and by that I mean catch handlers up the stack should not care if a new catch handler was introduced down the stack. This should also just apply in general to functions, a function either succeeded or failed, if a function internally fails but catches the error and turning it into a succeeded case, callers should be unaffected by this.
I mostly look at this in the eyes of if you're going to introduce a try / catch, it should encompass the whole function, and if it doesn't, move it to a new function. This is mostly because I think error handling quickly increases the complexity of a function. To me, there is just less mental overhead of having a function either just be transparent to errors (no errors can occur, or would only like to propagate the error), or is one that can actually handle the error.
Probably because you almost always want to specialize your exception handling
In my experience, if you're using exceptions, you often want different types of exceptions for different classes of errors, so you can handle/report them in different ways.
So I would say a catch(...) is a smell because you don't have any way of knowing/logging what kind of error occured.
@@cppweekly Hmm, I consider logging as part of the clean up process, so it's better served being hidden through a scope guard or a generic function. Something like
auto try_log_failure(auto callable)
{
try { return callable(); }
catch(const exception& e) { log_and_rethrow(e); }
catch(...) { log_and_rethrow("unexpected failure"); }
}
This way, in most cases, they don't particularly care about the type of exception being thrown reducing special cases. This would also increase the amount of swallowing exceptions if the whole purpose of the catch was to just log to consider it handled as they would now just be replaced with empty catches. Swallowing exceptions is also something I don't think isn't that bad if done with intent, which is hard to gauge.
I'm interested to see a code base that would regularly catch a specific type as it subverts my expectation of it being rare among the already rare amount of catch statements a program would have.
Exceptions are actually really competitive in some scenarios.
For instance, imagine if we had to check for an error code or something every time we called std::print. It would be a mess (too many if statements). Instead it uses exceptions so we only need to have a single try catch block for as many calls to std::print as we want.
I think this also shows a lack of good syntax to quickly handle errors as value. This is why the "?" operator in rust is nice, and i think it can (in part) solve this problem
@@Stowy In general, expected and exceptions are 2 sides of the same coin with various language support to make one or the other nicer to use.
In C++ specifically as of 2024, expected excels in transforming a single value in a more readable way with their monad functions. You could technically do the same right now for exceptions, but it'd be using the inside out syntax of doing `c(b(a()));`. If we had any form of UFCS / extension methods, exceptions could also do `a().b().c()` that is currently achieved via monads with expected.
Like you said rust having operator ? to do a implicit `if(failed) return;` is great while exceptions in general, is implicit. That being said, if we did reach a feature parity that can make both expected and exceptions be equally as ergonomic to use, I would still use exceptions merely from disliking the need to unwrap boxed values.
the good thing about exception is that you don't have to manage the passthrough of your dependencies errors. you don't even care what those errors are, you simply have to catch those that concerns you, and leave the rest be for others to manage.
can i make try-catch inside function, not main() ???
pretty much anywhere you want to
The one place where exceptions are indispensable is in constructors, which otherwise have no way of returning error conditions, at least if you're taking RAII remotely seriously. Otherwise they are one of many options for error reporting. In my view, throwing any exception type that is not a class derived from std::exception falls into the "you'd better have a REALLY good reason for this" category.
If making the object can throw, I prefer to use something like the "named constructor" pattern. The real constructor just moves its parameters into fields and otherwise does nothing. It can be private. Then public static methods actually do the work of constructing the data and passing it into the private constructor. Errors can be handled however one wants, error code, exceptions, std::optional, std::expected, or whatever.
I have two gripes with exceptions:
1. They normally make use of the heap (well, in all implementations I know of). Which means that libraries which use exceptions are grouped into "useable in environments where you can't or aren't allowed to use a heap" and "not useable in environments where you can't or aren't allowed to use a heap".
2. Some implementors or designers of certain libraries (looking at you, fstream classes) thought that it would be a good idea to use them for reporting errors which can be fairly common. Seriously, don't use exceptions for possibly common errors.
The exceptions in std::stream leave a lot to be desired.
Great one, but i played the video at 1.75x and I think this is the right speak rate for your videos.
I'm glad you enjoyed it. Lots of C++ Weekly watchers are not native English speakers, so feel free to watch at any speed that works for you!
The Lippincott function was covered earlier, yes, in episode 91
I still not use exception till now
I haven't seen jet a good C++ library/framework that uses exceptions good, I have seen Python modules that uses exceptions horribly, however in Php the framework Laravel uses Exceptions so well I doubt there is a better practical examples of how to use them.
Now I need to go check out Laravel to see what insight I can glean from them. 😄
@@KhalilEstell Simple stuff for example the functions you create run inside a try/catch space you don't need to create, and several functions may throw exceptions, and depending on the exception it redirect to something different (Laravel is web after all).
A more concrete example if you call findOrFail() and can't find, it throws the exception and the part that catches it redirect to a 404.
Another is for example you can define policies function and call gates; if a gate is call, it check the policy associated, if the policies returns false, the gate throws an exception and it returns a 403.
So you only think about the app logic assuming everything is working and if something goes wrong it gets auto-handled.
Jason, you could create Basic, Medium level, and Advanced c++ playlists, just to separate better your content.
deprecate exceptions