VSTS Annotations for C++: Beyond Just Documenting Method Behavior

Since Visual C++ 6.0 shipped in 1998, Microsoft's interest and commitment to C++ seem to have been slipping. C# has clearly replaced C++ as Microsoft's premier language for application development, and it feels as though C++ has been treated as an afterthought for many of the great new features that Visual Studio .NET has launched. Although this perception unarguably has been true to an extent, Microsoft is still very committed to C++; Redmond actually is the largest spender on ongoing development of C++ developer tools.

Microsoft's C++ commitment is evident in many of the Visual Studio Team System (VSTS) Developer Edition tools that are C++ only. For example, C++ has two types of static analysis available: C/C++-specific analysis and managed-assembly analysis, which developers can use on C++/CLI (as well as on C# and VB.NET) projects.

VSTS Annotations

One of the more interesting VSTS features that are available only to C++ developers is code annotations; these allow you to attach metadata to a method's parameters to explicitly define the correct usage of the method. Annotations allow the pre- and post-conditions for a method to be expressed directly within the source code file rather than a separate documentation file that can get lost or become outdated. Even more importantly, the compiler generates warnings for annotations that are violated. The ability to enforce annotations is a huge step forward from simply documenting that the callers of a method must perform some check of the parameters or return values before or after calling the method.

Annotations are simple to use, and they will look familiar to those who have used custom attributes in managed code. A simple example of an annotation is specifying whether a particular pointer parameter can be null. Consider a method that counts the number of occurrences of a particular character within a string—it would look something like the following:

#include <stdexcept>
#include "strsafe.h"

size_t CountChars(TCHAR* pString, TCHAR charToCount){
   if (pString == NULL)
      throw new std::invalid_argument("pString can't be null");

   size_t strLength = 0;
   const size_t maxStringLength = 100000;
   HRESULT hres = StringCchLength(pString,
                                  maxStringLength, &strLength);

   if (hres != S_OK)
      throw new std::invalid_argument("pString length is invalid");

   size_t retVal = 0;
   for(size_t ix = 0; ix < strLength; ++ix){
      if (pString[ix] == charToCount)
      ++retVal;
   }
   return retVal;
}

The problem with the checks as they are expressed in this method is that no error will be raised until runtime, which is the worst time to detect errors (or the second worst, if you count missing the error entirely and crashing the application or, worse, creating a security vulnerability). By adding a simple annotation to the method, you can include the check for the null string parameter at compile time:

#include "stdafx.h"
#include <stdexcept>
#include "strsafe.h"
#include <codeanalysis\sourceannotations.h>
using namespace vc_attributes;

size_t CountChars([Pre(Null=No)] TCHAR* pString, TCHAR charToCount){
   if (pString == NULL)

//rest of code omitted for brevity

When you compile this code with the /analyze switch and a potentially null string pointer is passed to the CountChars method, a compiler warning will be raised. As with all warnings, you can use the /WX command-line switch (Treat Warnings As Errors) to stop a build if you encounter an error. It is important to note that the annotation will have no effect if the /analyze switch is not on, and it also will have no effect at runtime, making it critical to still include the imperative code check for null parameters in addition to the declarative annotation.

The full range of standard annotations is available on the MSDN site. The annotations fall into two basic categories: pre-execution checks, like the one in the previous code example, and post-execution checks. Post-execution checks can be applied to both parameters and the return value of the function. For example, to specify that a caller must check the return value of a method, you can use the following annotation style:

[returnvalue:Post(MustCheck=Yes)] int MyFunction(){
   return 1;
}

The following code will not generate a warning when MyFunction is called:

int i = MyFunction();
if (MyFunction() > 0){}

This code will generate "warning C6031: Return value ignored: 'MyFunction'":

MyFunction();

One of the more interesting features of annotations is the ability to specify that one parameter in a method signature relates to another. This is particularly common in cases where the byte length of a pointer or string is specified. The following code example demonstrates this feature:

void UseBuffer(size_t buffLength,
               [Pre(Null=No, ElementSize="buffLength,
                    ValidElements="buffLength"")]
   void* buffer){
   ;
}

Using VSTS and SAL Annotations Together

One of the more disappointing aspects of the new VSTS annotations is that they do not integrate with the existing SAL annotations that Microsoft has been using internally, and which are publicly visible through the Windows SDK header files. These annotations are officially documented in the MSDN library, and some development organizations undoubtedly are using the SAL annotations rather than the new annotations offered through VSTS.

Because SAL annotations and VSTS annotations offer much the same functionality, and both are triggered through the same /analyze switch, very little distinguishes the two competing feature sets. SAL annotations are part of the C Runtime Libraries; hence, they don't have the same dependency on VSTS. In addition, SAL annotations are used extensively in the SDK header files, and developers may be more familiar with them. In contrast, VSTS annotations look neater, and Microsoft likely will commit a greater level of ongoing investment to them because they are part of a premium product like VSTS.

An explanation on the overlapping functionality is available in this forum post by Microsoft's Dave Bartolomeo. Basically, Dave says the VSTS annotations are the supported means for annotating your source code, and hints that the SDK header files may be changed to use the new annotation style.

About the Author

Nick Wienholt is an independent Windows and .NET consultant based in Sydney, Australia. He is the author of Maximizing .NET Performance from Apress, and specializes in system-level software architecture and development with a particular focus on performance, security, interoperability, and debugging. Nick can be reached at NickW@dotnetperformance.com.



Comments

  • Clutter and gibber

    Posted by Efitap on 04/18/2006 08:12am

    I'd rather see some javadoc-ish annotation system as opposed to cluttering up function headers with this system. In my opinion, this is a poor solution, it's non-ansi, non-portable, and hence, probably quite non-reusable in these days of growing cross-platformity. The idea is sweet, but the implementation is trash.

    Reply
Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • This paper introduces IBM Java on the IBM PowerLinux 7R2 server and describes IBM's implementation of the Java platform, which includes IBM's Java Virtual Machine and development toolkit.

  • The explosion in mobile devices and applications has generated a great deal of interest in APIs. Today's businesses are under increased pressure to make it easy to build apps, supply tools to help developers work more quickly, and deploy operational analytics so they can track users, developers, application performance, and more. Apigee Edge provides comprehensive API delivery tools and both operational and business-level analytics in an integrated platform. It is available as on-premise software or through …

Most Popular Programming Stories

More for Developers

Latest Developer Headlines

RSS Feeds