Using templates to convert calls to strcpy and other CRT functions

Some teams, and the debugger is definitely one of them, make heavy use of the C Runtime (CRT) string manipulation functions. Before VS 2003 shipped, we spent a lot of time converting calls from the old insecure functions, to calls to the equivalent function from strsafe.h. VS 2005 is now coming along with bigger changes. There are secure versions of even more functions (example: _splitpath, _itoa), and by default all the insecure functions are deprecated, so you have to fix all your code. Today I want to talk about my recommendations for this.

First, don't just disable the deprecation; strcpy is evil. 

My other recommendation is to use templates instead of hand editing every call site. How do you do this? Like this:

template <int CCH>

inline HRESULT SafeStrCatA(char (&szBuffer)[CCH], const char *szStr)

{

      C_ASSERT(CCH > 0);

      return StringCchCatA(szBuffer, CCH, szStr);

}

Now if I have code that does:

char foo[12];

strcpy(foo, bar);

strcat(foo, jam);

I can convert this code with just a simple search and replace. I don't even need to open the file in the editor. The other advantage to templates is that you will never have a bug where you got the buffer size wrong. Templates do fall down if the buffer was allocated on the heap, or if your code is doing pointer arithmetic to calculate the buffer. For the debugger code base, about 70% of the call sites converted automatically convert. Your mileage may vary. For more information, see MSDN.

BTW: Here is how do fix _splitpath, or other functions that have optional buffers:

struct _null_t {};

class CHAR_BUFFER

{

public:

      char* const pBuffer;

      const size_t cch;

public:

      CHAR_BUFFER(_null_t* null) : pBuffer(NULL), cch(0)

      {

      }

      template <int CCH_BUFFER>

      CHAR_BUFFER(char (&sz)[CCH_BUFFER]) : pBuffer(sz), cch(CCH_BUFFER)

      {

            C_ASSERT(CCH_BUFFER > 0);

      }

      CHAR_BUFFER(char* p, size_t c) : pBuffer(p), cch(c)

      {

      }

private:

      // not defined

      CHAR_BUFFER();

      CHAR_BUFFER(const CHAR_BUFFER&);

      CHAR_BUFFER& operator=(const CHAR_BUFFER&);

};

inline errcode Safe_splitpath(

      IN const char* szPath,

      IN OUT const CHAR_BUFFER& drive,

      IN OUT const CHAR_BUFFER& dir,

      IN OUT const CHAR_BUFFER& file,

      IN OUT const CHAR_BUFFER& ext

      )

{

      return _splitpath_s(

            szPath,

            drive.pBuffer, drive.cch,

            dir.pBuffer, dir.cch,

            file.pBuffer, file.cch,

            ext.pBuffer, ext.cch

            );

}