LabWindows/CVI

cancel
Showing results for 
Search instead for 
Did you mean: 

What is a good error checking strategy?

Almost every function returns a status whether the function has succeeded or not. If I would check every single function's status on exit, the whole program would look something like

if ((Status = function1()) != OK) ErrorMessage(Status);
if ((Status = function2()) != OK) ErrorMessage(Status);
if ((Status = function3()) != OK) ErrorMessage(Status);

Apart from the compiler going nuts over an assignment in a conditional expression all the time (resulting in turning this checking off, what can cause real errors to go unnoticed), the readbility of what the code is exactly supposed to do decreases a lot IMO.

My solution is to do only error checking on functions when I think there is a chance for error, and then give a specific error message. However, this is not very consistent. Are there any strategies you guys want to share?

Attached is my Malloc() routine, which gives me instantly an indication what goes wrong when I call malloc() in a wrong way. Any comments to stupidities on my part gladly welcomed.

This routine would be called like

float *TestVar = 0;
if (!Malloc(TestVar, NrOfFloats*sizeof(float), "main()", "TestVar")) return -1;

The Malloc() routine requires the pointer to be 0, to avoid allocating memory to a pointer that hasn't been freed. This brings the extra burden of HAVING to initialize every pointer you will use with Malloc(), but it also gives some extra debugging capabilities.
0 Kudos
Message 1 of 8
(4,445 Views)
This is my 2c.

It is correct you cannot check every single line of code for errors. Besides it, not all errors are able to crash the application and not all of them need to stop the program.

In my applications I tend to separate the errors derived from user actions from those coming from programmer actions: while I make all efforts in giving to the first ones good explanations, the second needs to be checked before the application is licenced.

Based on the ErrChk and NullChk macros included in toolbox, I have developed some error checking macros for file I/O, serial communication, DAQ functions and so on which allow me to react on severe errors. For example: a DAQ checking macro that jumps to a fixed label to stop a test execution and another that simply exits with a break statement (useful when included in a loop after which I need to clear the acquisition / generation tasks in progress). In all those macros I included the line of code on error (using the __LINE__ macro) and in case the error must be reported to the operator I format a string with the error code, line and function on error (__FUNCTION__ macro is very useful for this!) and some explanation (obtained with GetGeneralErrorString, GetNIDaqErrorString, GetFmtIOErrorString... functions).

Last item: some time it is not safe to simply break down the code with a MessagePopup (this is when there are motors running or parts live with high current / voltage on them and so on): in this case I have developed a warning function issued with a PostDeferredCall that receives all parameters related to error encountered and finally displays a panel with all these datas or writes them to a file after the application has safely ended the test in progress.


Finally, I'm sorry for not having answered to your question before but I missed it. I find sharing our programming tips is very useful, so everyone which will comment this or add his suggestions will be well accepted.


Proud to use LW/CVI from 3.1 on.

My contributions to the Developer Community
________________________________________
If I have helped you, why not giving me a kudos?
Message 2 of 8
(4,380 Views)
What you need is code that is easly debugged and code that runs well and that meand #defines! Try this for an example

#ifdef _CVI_DEBUG_
#define malloc(size) malloc_check(size, __FUNCTION__, __LINE__)
#endif

void* malloc_check(int size, const char* func, int line)
{
void* ptr;
char ErrorMsg[100];

if (size==0)
return 0;

ptr = malloc(size) ;

if (!ptr)
{
sprintf(ErrorMsg, "Memory Allocation Error\n\tFunction %s\n\tLine:%d", func, line) ;
MessagePopup(ErrorMsg, "Debug Error") ;
}
}

I also use this way to overide free and then I can keep track of all the memory allocated and freed by putting the pointers on a stack, then at the end of the program you can output a list of all the lines where you have memory leaks. Works Great! Best part is it does not affect the final product either, and your code looks normal.

You can Play the Same Game with Error Checking:

using your example:
#define ERRCHK(x, __FUNCTION__, _LINE__) (if (x!=OK) ErrorMessage(x);)

using #define 's efficently in your code can really be a good debugger! Give it a try.
0 Kudos
Message 3 of 8
(4,362 Views)
A side note to my last post if you try the example the #define malloc(size) malloc_check(size, __FUNCTION__, __LINE__) can note appear directly above the function like I have it that was just for example. If you do that the program will crash because the malloc in the malloc check will just keep calling itself, you must have those two seperated. Put the #define in a header and the function in a .c that does NOT include the header. Also to allocate memory now its the same as before:

int* ptr;
ptr = malloc(x)

and in debug mode the complier automaically call your new malloc_check you can do anything you want there. You should really read about what can be done with complier directeves, there is a lot of power there that most people just overlook.
0 Kudos
Message 4 of 8
(4,359 Views)
I'm also looking for a good strategy to do error checking. I noticed that CVI provided a macro definition of "tsErrChk". I think this could be modified and reused for our program. Here I have a question about the mark "\", I'm some confused about its function and how to use it, hope I can get your help. Thanks! Jacky #define tsErrChk(fCall)\ \ if ((error = (fCall)) < 0) {\ TS_GetOLEErrorMsg(error, &errorInfo, errMsg, 0, 0, ERRMSG_SIZE);\ error = (error == DISP_E_EXCEPTION ? errorInfo.sCode : error);\ if(error < 0) goto Error;\ } else
0 Kudos
Message 5 of 8
(4,116 Views)
A #define statement normally terminates at the end of the line beginning with the '#'; the '\' character can be used at the end of the line to tell the pre-processor to continue using the next line as if it was part of the same #define . This way you can extend a #define to cover as many lines as you like, by using this continuation character.
 
JR
0 Kudos
Message 6 of 8
(4,108 Views)

Ooo, this is a really old topic, but it's one I'm pretty passionate about.  

 

Here's a few good resources:

 

The latter one I have not used yet.  It's a fascinating piece of software that I'd like to try one day.

0 Kudos
Message 7 of 8
(3,229 Views)

  • Roerto's library for handling (for the life of me, I can't seem to find it now!)

It's here Smiley Wink

 

...and I have to take a look to that E4C library!



Proud to use LW/CVI from 3.1 on.

My contributions to the Developer Community
________________________________________
If I have helped you, why not giving me a kudos?
Message 8 of 8
(3,227 Views)