|
Perhaps more unhelpful is an error message that was context related and then got copy-pasted into a totally unrelated function i.e "Error in function A!" when it is really in function B
|
|
|
|
|
In C/C++ this can be solved by LogError("Error in function %s!", __FUNCTION__);. A better solution is to do logging with macros that has several advanatages: you can easily remove logging depending on log level (for example you can remove only LOG_WARNINGS but leaving LOG_ERRORS in code in release builds) and you can easily collect filenames, line numbers and function names automatically:
#if LOG_WARNING_ENABLED
#define LOG_WARNING(format, ...) g_Logger.Log(ELogLevel_Warning, __FILE__, __LINE__, __FUNCTION__, format, ##__VA_ARGS__)
#else
#define LOG_WARNING(format, ...)
#endif
|
|
|
|
|
I worked with a guy that would litter his code with assert(Date.Now < [next tuesday]), so that he would remember to remove his debug code before it hit production except when he released his assert statements to production.
|
|
|
|
|
Hm, if i remember correct in c++ even if you have asserts in the code in build due of the compiler optimizations they don't get executed with validate . I don't see the problem with putting asserts and validates in code
Microsoft ... the only place where VARIANT_TRUE != true
|
|
|
|
|
In release builds usually NDEBUG is defined and with NDEBUG defined assert macros expand to nothing.
|
|
|
|
|
But an assert usually isn't compiled into release builds. One of our projects currently using debug, profile and release (and sometimes more) configs internally.
relase: obvoius.
profile: builds used by the qa and internal workers. this is essentially a release build with optimizations but asserts are on.
debug: obvious.
Whenever possible its better to put in compile time errors/static asserts. In my opinion the assert you described should be a comment, a tagged task or TODO. I usually mark such codepieces with "// TODO:" or "// HACK:" or "// FIXME:" or "// TRON_TODO:" if I want to differentiate this from others' todo tags. This is easy to find with a global search in any language. In eclipse you can define your own tasks and the "tasks" view is able to collect these tags when you compile/autocompile your java code...
|
|
|
|
|
At least it gives some indication of something, as opposed to an empty catch block grrrrrrr!
|
|
|
|
|
Could be worse.. something like (in C):
exit(0);
Now there is a useful error message.. oh, wait! what message?
The sad thing is that a long time ago I came across a library that did this (just outright exited the program, rather than return an error code to the caller).
|
|
|
|
|
There are several different kind of runtime errors that are fatal and unpractical to handle in your code. Some of these errors (like null pointer exception/access violation/SIGSEGV/SIGBUS and similar fatal errors) must be handled by your top level crash/exception/signal handlers. Some other errors (like can't create thread, out of memory) are practical to handle by calling your own critical error handler function like CRITICAL("Couldn't create thread!"). This critical handler never returns, logs an error message or shows a messagebox and debugbreaks or terminates the program depending whether you run the program in a debugger or not. What would you do if you call the Start() method of a thread and it returns false??? In most programs you simply cant handle such serious errors so its better to return with nothing (void) from the Start() method and to handle the error inside the Start() method by calling CRTICAL(). What do you do if the pthread_mutex_init() or pthread_mutex_lock() call fails??? Because these have return value! In my opinion these functions shouldn't return anything because handling these errors is impractical, the library should inform you with some debug breaks or internal critical handlers. For example in debug builds/debug sessions pthread_mutex_destroy() should debug break in debug sessions or log/fatal exit in release if you try to destroy a mutex that is held by a thread, pthread_mutex_init should also do the same. I convert these errors to CRITICAL() in my encapsulated mutex classes.
CRITICAL() is a much better approach then seeing the accumulating code that dumbly tries to handle for example thread creation errors here and there (usually untested error handlers that would crash or deadlock sooner or later after an unsuccessful thread creation). Same is true for memory management. If these errors happen than something really wrong is going on with the machine/OS. Instead of handling these errors its better to write and design the program to operate well if the given amount of resources are available (memory, video memory, cpu performance, free disk space, ...)
Probably that exit(0) code should be replaced with a CRITICAL() call.
|
|
|
|
|
While there are certainly some errors that can't be handled well/predictable (e.g. if memory was corrupted by a buffer overrun, then attempting to even show an error dialog may cause yet another error). But I don't think aborting the program should be done just because a thread couldn't be created (since it is generally an initialization error, like opening some required and missing resource file, not corruption making the running image unstable). If it was a problem beyond that (machine hardware or OS failing), then it doesn't really matter what any application does since you are likely going to have to hardboot your system anyway.
If I was using some editing application that internally spawns threads to perform certain [user triggered] operations, I don't want it exiting and loosing all my in-memory edits just because it couldn't create a thread to perform an operation that has yet to be run. I'd want an error message so that I have a chance to save my work and possibly restart the program. Worst case is trying to save also fails with another error and I'm out of luck, but otherwise being able to save is a better option.
And if the pthread functions failing was really such an unpassable wall, then why would they even return error codes at all and not abort the program for you? It is because they expect programmers to handle the error (even if that handling is _sometimes_ limited). There could be many reasons why these functions fail depending on the library implementation (or maybe just a localized coding error in the application itself).. so assuming they are always catastrophic reasons is just a narrow viewpoint.
|
|
|
|
|
I don't think that someone would want to handle the failure of core functions and thread creation is quite a core feature in my opinion. Even if you would want to handle errors after every single line of code you would fail. The solution to your problem is building an automatic document save/backup function into your crash handlers/critical function. This way you guarded your program not only against thread creation errors but a lot of other bugs/crashes too!
BTW, in most of my programs I usually create all needed threads at program startup (in pools) and destroy them at cleanup. In this case a CRITICAL is valid even without document saver. Most multithreaded programs work with a fixed or predictable number of threads that you can precreate.
|
|
|
|
|
Sadly, I have seen too much code like that. Having a useless message is marginally more useful than having nothing there.
Just because the code works, it doesn't mean that it is good code.
|
|
|
|
|
In my C/C++ code I often write similar things: assert statements. ASSERT -> "It should never happen that the specified expression evaluates to false". Of course ASSERT is usually compiled only into some non-release configurations. I use different kind of assert statements, I call one of these the "This should never happen" assert: This is an assert that always fires unconditionally with a specified message when executed. For example assert_message("Unhandled EMode enum member");
enum EMode
{
EMode_mode1,
EMode_mode3,
};
void Func(EMode mode)
{
switch (mode)
{
case EMode_mode1:
break;
case EMode_mode2:
break;
default:
assert_message("Unhandled EMode enum member!");
break;
}
}
If someone extends the enum with a new member (and lets say the functions that use the enum are not next to the enum in your files) and some codepieces are not updated along with the enum then at least you get a runtime error sooner or later in non-release builds. Of course there are a lot of other places where "This should never happen" asserts are useful.
|
|
|
|
|
Code1:
Boolean DoSomething(string[] values)
{
foreach (string s in values)
if (s == "ABC")
return true;
return false;
}
Code2:
Boolean DoSomething(string[] values)
{
bool retValue = false;
foreach (string s in values)
if (s == "ABC")
retValue=true;
return retValue;
}
in the above 2 codes which code you will suggest and why? waiting for your valuable comments.
Thanks
--RA
|
|
|
|
|
The first will be faster, as it will exit as soon as finding "ABC"
Alternatively,
return values.Contains("ABC")
|
|
|
|
|
You left out the ;
-= Reelix =-
|
|
|
|
|
Reelix wrote: You left out the ;
Well, the benefit of your first option is that it is faster as has already been mentioned. Sometimes built-in's are faster.
For that reason you might want to look at the built-in function. I read that someone thought the bubble sort put in an article was very efficient. I'm going "Oh G.., save us from inexperienced programmers" Built the bubble sort, a slightly more efficient version, and my binary sort routine I (re)wrote after seeing that #%#$@. I stopped testing performance of the bubble sort at 200K (over 2 minutes) I threw in the built in sort routine too. Both were sorting 200K in sub-second times. After getting up there in size, the built-in was performing in about 2/3 the time my routine was.
In Big O, the bubble was N^2 and time tests matched that estimated. I stopped testing mine at 150M (space ran out at 200M) Built-in 29 seconds, mine 49 seconds, slightly faster than 2/3. I estimated the bubble would finish in 750 squared times 130 seconds.
|
|
|
|
|
Personally I prefer the second option, as there's only a single return from the function.
That said, there's a whole bunch wrong with the code... or at least not good practice. (Yes, I realise it's just an example.)
|
|
|
|
|
I don't want to see any of these running, because both are bad style. Inline-Statements, no brackets, bad string comparison. They are EVIL!
|
|
|
|
|
I prefer the second method. I try not to have multiple places where a function can return.
There are only 10 types of people in the world, those who understand binary and those who don't.
|
|
|
|
|
I think that's an unnecessary restriction - I prefer to do all my validation code / user notification en mass at the top of a method, and exit immediately.
if (!userGotHisNameRight)
Report It
return
if (!userManganagedHisAddressOK)
Report It
return
if (...)
...
Actual work the method is supposed to do
The alternataive being:
if (!userGotHisNameRight)
Report It
else if (!userManganagedHisAddressOK)
Report It
else if (...)
...
else
{
Actual work the method is supposed to do
}
The universe is composed of electrons, neutrons, protons and......morons. (ThePhantomUpvoter)
|
|
|
|
|
Ya, I know. Some people like it and some don't. Generally speaking the amount of time saved by returning in various places is negligible.
There are only 10 types of people in the world, those who understand binary and those who don't.
|
|
|
|
|
ryanb31 wrote: Generally speaking the amount of time saved by returning in various places is negligible.
Generalization is Bad. what would you say if there was a database access on that loop? or a file read/write? a service call? are those so rare that they can't be considered "general"?
I'm ok with having one single return point, provided that the method doesn't do any significant work. the general rule should be to exit as soon as you finish the work or realize there's nothing more to be done.
I'm brazilian and english (well, human languages in general) aren't my best skill, so, sorry by my english. (if you want we can speak in C# or VB.Net =p)
"Given the chance I'd rather work smart than work hard." - PHS241
"'Sophisticated platform' typically means 'I have no idea how it works.'"
|
|
|
|
|
But you're missing the fact that you can exit a loop when you find what you need. You don't have to continue processing.
There are only 10 types of people in the world, those who understand binary and those who don't.
|
|
|
|
|
What if you have two nested loops? It's a lot harder to exit them both...
The universe is composed of electrons, neutrons, protons and......morons. (ThePhantomUpvoter)
|
|
|
|