3 ms·
> Totally ignoring errors leaves you with crashy garbage that does not accomplish your goal of high uptime/availability/not corrupting data in C++ - I'd wager y
by candiodari 9y ago
> Totally ignoring errors leaves you with crashy garbage that does not accomplish your goal of high uptime/availability/not corrupting data in C++ - I'd wager you're dealing with safer languages to type that with a straight face! Even there, though, you probably write code to at least handle null references and other edge cases. You might not trigger fatal assertion dialogs, you might not log to some fancy telemetry system, you might not even log at all - but something to handle error conditions.
That is exactly what I mean. Defensive programming is erroring out and refusing to do anything from that point forward (think of it like an uncaught exception with less information). "Total" programming is doing the best you can, given what's available. Generally if I am unable to do something, or look something up, I'll return a partially filled out request, log it (logs are rate-limited VERY early in the logging process), and increase a counter created for that particular case.
(I do use C++ often, but we use variable guards to guard against null pointers, use-after-free, and sharing (unless explicit)).
> A fun trick from gamedev: "Assertion" macros that force you to include an error handling statement
Link ? I'm interested.
- jsjohnst 9y ago> Defensive programming is erroring out and refusing to do anything from that point forward (think of it like an uncaught exception with less information) I’m not sure where you got that impression, but it’s not the generally accepted definition. Defensive programming does not imply crash/abort/exit, it just simply implies a style of programming where you don’t trust “things will always be ok”. That could involve checking every input for correctness, checking every function output, handling all exceptions, etc. Yes, a defensive method could throw an exception on invalid input, but if the entire program is written defensively, then it should be handled and potentially continued.
- MaulingMonkey 9y ago> Link ? I'm interested. The examples I'm thinking of are all closed source, but here's a trivial implementation on the simpler end of the scale in my favorite style/variation of the idea: #include <iostream> #include <sstream> namespace xyz { constexpr bool logFails = true; } #define XYZ_CHECK_MSG(cond, msg, expr) if (!(cond)) { \ if (::xyz::logFails) { ::std::cerr << msg << ::std::endl; } \ expr; \ } else ((void)0) /* ; */ #define XYZ_CHECK(cond, expr) XYZ_CHECK_MSG(cond, "XYZ_CHECK(" #cond ", ...) failed", expr); void increment (long * i) { XYZ_CHECK(i, return); // nonfatal ++*i; } int main(int argc, char ** argv) { XYZ_CHECK_MSG(argc > 1, "Usage: " << argv[0] << " [number] [number]", std::exit(1)); // super fatal for (int iArg = 1; iArg < argc; ++iArg) { const char * const arg = argv[argc]; char * argEnd = nullptr; long n = std::strtol(argv[iArg], &argEnd, 10); // doesn't even end the function - "continue" could be replaced with "n=0" as well! XYZ_CHECK_MSG(arg != argEnd && *argEnd == '\0', "Argument " << iArg << " was not a number", continue); increment(&n); std::cout << n << "\n"; } } Technically, if you don't want an error handling case, you don't have to provide one, but that's a bit explicit - you won't forget to at least think about it unless you really work at establishing such a bad habit: XYZ_CHECK_MSG(condition, "Something bad happened, but let's just continue on our way.", ); More complicated versions of this might define additional macro params to: 1) Define a name and default severity - such that you can squelch false positives that snuck into the build before you can create and deploy a new one, or enable slower debug checks that might be skipped by default. 2) Configure where the logs go or what categories the messages will be filtered into. 3) Tag JIRA tasks or bug numbers to automatically reopen, or specific authors to alert. Although these can also be handled in a more orthogonal manner.