Thursday, January 20, 2011

MFC Assertions

MFC defines the ASSERT macro for assertion checking. It also defines the MFC ASSERT_VALID and CObject::AssertValid for checking the internal state of a CObject-derived object.


The MFC ASSERT macro halts program execution and alerts the user if the argument (an expression) evaluates to zero or false. If the expression evaluates to a nonzero, execution continues.

When an assertion fails, a message dialog box displays the name of the source file and the line number of the assertion. If you choose Retry in the dialog box, a call to AfxDebugBreak causes execution to break to the debugger. At that point, you can examine the call stack and other debugger facilities to determine the cause of the assertion failure. If you have enabled Just-in-time debugging, the dialog box can launch the debugger if it was not running when the assertion failure occurred.

The following example shows how to use ASSERT to check the return value of a function:

int x = SomeFunc(y);
ASSERT(x >= 0); // Assertion fails if x is negative

You can use ASSERT with the IsKindOf function to provide type checking of function arguments:
ASSERT( pObject1->IsKindOf( RUNTIME_CLASS( CPerson ) ) );

The ASSERT macro catches program errors only in the Debug version of your program. The macro produces no code in the Release version. If you need to evaluate the expression in the Release version, use the VERIFY macro instead of ASSERT.

We are discussing about assertions; first we need to know what's Assert in basic.

An assertion statement specifies a condition that you expect to hold true at some particular point in your program. If that condition does not hold true, the assertion fails, execution of your program is interrupted, and the Assertion Failed dialog box appears.

This is a CObject class method; and can be used like,
AssertValid(): This method is used to check your class. Because an inherited class is usually meant to provide new functionality based on the parent class, when you create a class based on CObject, you should provide a checking process for your variables. In this case you should provide your own implementation of the AssertValid() method. You can use it for example to check that an unsigned int variable is always positive.


CObject::AssertValid

virtual void AssertValid( ) const;


Defination  :  AssertValid performs a validity check on this object by checking its internal state. In the Debug version of the library, AssertValid may assert and thus terminate the program with a message that lists the line number and filename where the assertion failed.

When you write your own class, you should override the AssertValid function to provide diagnostic services for yourself and other users of your class. The overridden AssertValid usually calls the AssertValid function of its base class before checking data members unique to the derived class.

Because AssertValid is a const function, you are not permitted to change the object state during the test. Your own derived class AssertValid functions should not throw exceptions but rather should assert whether they detect invalid object data.

The definition of “validity” depends on the object’s class. As a rule, the function should perform a “shallow check.” That is, if an object contains pointers to other objects, it should check to see whether the pointers are not null, but it should not perform validity testing on the objects referred to by the pointers.


Example
See CObList::CObList for a listing of the CAge class used in all CObject examples.

// example for CObject::AssertValid
void CAge::AssertValid() const
{
     CObject::AssertValid();
     ASSERT( m_years > 0 );
     ASSERT( m_years < 105 );
}


Visual C++ supports assertion statements based on the following constructs:
•MFC assertions for MFC program.
•ATLASSERT for programs that use ATL.
•CRT assertions for programs that use the C run-time library.
•The ANSI assert function for other C/C++ programs.

You can use assertions to:
•Catch logic errors.
•Check results of an operation.
•Test error conditions that should have been handled.

MFC and C Run-Time Library Assertions

When the debugger halts because of an MFC or C run-time library assertion, it navigates to the point in the source file where the assertion occurred (if the source is available). The assertion message appears in the Output window as well as the Assertion Failed dialog box. You can copy the assertion message from the Output window to a text window if you want to save it for future reference. The Output window may contain other error messages as well. Examine these messages carefully, because they provide clues to the cause of the assertion failure.

Through the liberal use of assertions in your code, you can catch many errors during development. A good rule is to write an assertion for every assumption you make. If you assume that an argument is not NULL, for example, use an assertion statement to check for that assumption.

_DEBUG
Assertion statements compile only when _DEBUG is defined. When _DEBUG is not defined, the compiler treats assertions as null statements. Therefore, assertion statements have zero overhead in your final release program; you can use them liberally in your code without affecting the performance of your Release version and without having to use #ifdefs.

Side Effects of Using Assertions

When you add assertions to your code, make sure the assertions do not have side effects. For example, consider the following assertion:

ASSERT(nM++ > 0); -- Don't do this!

Because the ASSERT expression is not evaluated in the Release version of your program, nM will have different values in the Debug and Release versions. In MFC, you can use the VERIFY macro instead of ASSERT. VERIFY evaluates the expression but does not check the result in the Release version.

Be especially careful about using function calls in assertion statements, because evaluating a function can have unexpected side effects.

ASSERT ( myFnctn(0)==1 ) –- unsafe if myFnctn has side effects

VERIFY ( myFnctn(0)==1 ) –- safe

VERIFY calls myFnctn in both the Debug and Release versions, so it is safe to use. You will still have the overhead of an unnecessary function call in the Release version, however.

Some generall questions and solution about Assertions.
(I've collected this from various resourses and MSDN ..... )

Prob One:  In MFC, why do I get an assertion when my thread calls a function in the main thread?


Solution and Explanation :The MFC message processing code is not thread-safe. So when a secondary thread calls anything in the main thread that affects a CWnd-derived object there is a danger of data corruption within MFC. To guard against this MFC uses thread local storage and liberal ASSERTs to warn you that what you are trying to do can't work reliably. There are two safe alternatives for updating a main thread window from a secondary thread.

In simple cases you can bypass MFC and take advantage of the fact that the Win32 API is thread-safe. Your secondary thread can safely use an hwnd passed from the main thread and call a Win32 function to force many kinds of updates:

// force repaint of a window in the main thread
::InvalidateRect(hwnd, NULL, TRUE);

// change the text in any control
::SetDlgItemText(hwnd, IDC_CONTROL, "Hello Bro");

When you want to force complex actions such as UpdateAllViews or UpdateData you need the involvement of the main thread's MFC code. In such cases you can "call" the main thread from your secondary thread in a safe and synchronized fashion by sending or posting a user-defined message to the main thread.

Assertion in managed code

An assertion, or Assert statement, tests a condition, which you specify as an argument to the Assert method. If the condition evaluates to true, no action occurs. If the condition evaluates to false, the assertion fails. If you are running under the debugger, your program enters break mode.


•The Debug.Assert Method
•Side Effects of Debug.Assert
•Trace and Debug Requirements
•Assert Arguments
•Customizing Assert Behavior
•Setting Assertions in Configuration Files

In Visual Basic and Visual C#, you can use the Assert method from either the Debug class or the Trace class (part of the System.Diagnostics namespace). Debug class methods are not included in a Release version of your program, so they do not increase the size or reduce the speed of your release code. For more information, see Debug Class and Trace Class.

Managed Extensions for C++ does not support the Debug class methods. You can achieve the same effect by using the Trace class with conditional compilation (#ifdef DEBUG... #endif).

The Debug.Assert Method

Use the Debug.Assert method freely to test conditions that should hold true if your code is correct. For example, suppose you have written an integer divide function. By the rules of mathematics, the divisor can never be zero. You might test this using an assertion:

[Visual Basic .NET]
Function IntegerDivide(ByVal dividend As Integer, ByVal divisor As Integer) As Integer
Debug.Assert(divisor <> 0)
Return CInt(dividend / divisor)
End Function

[C#]
int IntegerDivide ( int dividend , int divisor )
{ Debug.Assert ( divisor != 0 );
return ( dividend / divisor ); }

When you run this code under the debugger, the assertion statement will be evaluated, but in the Release version, the comparison will not be made, so there is no additional overhead.

Let's look at another example.
 Suppose you have a class that implements a checking account. Before you withdraw money from the account, you want to make sure that the account balance is sufficient to cover the amount you are preparing to withdraw. You might write an assertion to check the balance:

[Visual Basic .NET]
Dim amount, balance As Double
balance = savingsAccount.balance
Debug.Assert(amount <= balance)
SavingsAccount.Withdraw(amount)

[C#]
float balance = savingsAccount.Balance;
Debug.Assert ( amount <= balance );
savingsAccount.Withdraw ( amount );

Note that calls to the Debug.Assert method disappear when you create a Release version of your code. That means that the call that checks the balance will disappear in the Release version. To solve this problem, you should replace Debug.Assert with Trace.Assert, which does not disappear in the Release version:

[Visual Basic .NET]
Dim amount, balance As Double
balance = savingsAccount.balance
Trace.Assert(amount <= balance)
SavingsAccount.Withdraw(amount)

[C#]
float balance = savingsAccount.Balance;
Trace.Assert ( amount <= balance );
savingsAccount.Withdraw ( amount );

Calls to Trace.Assert, unlike calls to Debug.Assert, add overhead to your Release version.

Side Effects of Debug.Assert

When you use Debug.Assert, make sure that any code inside the Assert does not change the results of the program if the Assert is removed. Otherwise, you may accidentally introduce a bug that only shows up in the Release version of your program. Be especially careful about Asserts that contain function or procedure calls, such as the following example:

[Visual Basic .NET]
Debug.Assert (meas(i) <> 0 )  //unsafe code


[C#]
Debug.Assert (meas(i) != 0 );  // unsafe code

This use of Debug.Assert may appear safe at first glance, but suppose the function meas updates a counter each time it is called. When you build the Release version, this call to meas is eliminated, so the counter will not get updated. This is an example of a function with a side effect. Eliminating a call to a function that has side effects could result in bug that only appears in the Release version. To avoid such problems, do not place function calls in a Debug.Assert statement. Use a temporary variable instead:

[Visual Basic .NET]
temp = meas( i )
Debug.Assert (temp <> 0)

[C#]
temp = meas( i );
Debug.Assert ( temp != 0 );

Even when you use Trace.Assert, you might still want to avoid placing function calls inside an Assert statement. Such calls should be safe, because Trace.Assert statements are not eliminated in a Release build. However, if you avoid such constructs as a matter of habit, you are less likely to make a mistake when you use Debug.Assert.

Trace and Debug Requirements

For Trace methods to work, your program must have on of the following at the top of the source file:
•#Const TRACE = True in Visual Basic
•#define TRACE in Visual C# and Managed Extensions for C++

Or your program must be built with the TRACE option:
•/d:TRACE=True in Visual Basic
•/d:TRACE in Visual C# and Managed Extensions for C++

If you create your project using the Visual Studio .NET wizards, the TRACE symbol is defined by default in both Release and Debug configurations. By contrast, the DEBUG symbol is defined by default only in the Debug build.

If you need to use the Debug methods in a C# or Visual Basic Release build, you must define the DEBUG symbol in your Release configuration.

Managed Extensions for C++ does not support the Debug class methods. You can achieve the same effect by using the Trace class with conditional compilation (#ifdef DEBUG... #endif). (You can define these symbols in the Property Pages dialog. For more information, see Changing Project Settings for a Visual Basic Debug Configuration or Changing Project Settings for a C or C++ Debug Configuration.)

Assert Arguments
Trace.Assert and Debug.Assert take up to three arguments. The first argument, which is mandatory, is the condition you want to check. If you call Trace.Assert or Debug.Assert with only one argument, the Assert method checks the condition and, if the result is false, outputs the contents of the call stack to the Output window. The following examples show Debug.Assert and Trace.Assert used with one argument.

[Visual Basic .NET]
Debug.Assert(stacksize > 0)
Trace.Assert(stacksize > 0)

[C#]
Debug.Assert ( stacksize > 0 );
Trace.Assert ( stacksize > 0 );

The second and third arguments, if present, must be strings. If you call Trace.Assert or Debug.Assert with two or three arguments (one or two strings), the Assert method checks the condition and, if the result is false, outputs the string or strings. The following examples show Debug.Assert and Trace.Assert used with two or three arguments:

[Visual Basic .NET]
Debug.Assert(stacksize > 0, "Out of stack space")
Trace.Assert(stacksize > 0, "Out of stack space")

[C#]
Debug.Assert ( stacksize > 0, "Out of stack space" );
Trace.Assert ( stacksize > 0, "Out of stack space" );

[Visual Basic .NET]
Debug.Assert(stacksize > 0, "Out of stack space. Bytes left:" , Format(size, "G"))
Trace.Assert(stacksize > 0, "Out of stack space. Bytes left:" , Format(size, "G"))
Trace.Assert(stacksize > 0, "Out of stack space. Bytes left:", "inctemp failed on third call" )

[C#]
Debug.Assert ( stacksize > 100, "Out of stack space" , "Failed in inctemp" );
Trace.Assert ( stacksize > 0, "Out of stack space", "Failed in inctemp" );

Customizing Assert Behavior
If you run your application in user-interface mode, the Assert method displays the Assertion Failed dialog box when the condition fails. The actions that occur when an assertion fails are controlled by the Debug.Listeners or Trace.Listeners property. You can customize the output behavior by adding a TraceListener object to the Listeners collection, by removing a TraceListener from the Listeners collection, or by overriding the Fail method of an existing TraceListener to make it behave differently. For example, you could override the Fail method to write to an event log instead of displaying the Assertion Failed dialog box. For more information, see TraceListener Class. To customize the output in this way, your program must contain a listener, and you must inherit from the TraceListener and override its Fail method. For more information, see Debug.Fail or Trace.Fail.

Setting Assertions in Configuration Files
If you prefer, you can set an assertion in your program configuration file instead of in your code. For details, see Debug.Assert Method (Boolean) or Trace.Assert Method (Boolean).




No comments:

Post a Comment

Health Benefits of Cashews

  Benefits of Cashews. Healthy food is an integral part of healthy body. Regular exercises such as Yoga and healthy diet is important to...