Table of Contents
C++11 (formerly known as C++0x) is the most recent version of the standard of the C++ programming language. It was approved by ISO on 12 August 2011, replacing C++03. C++11 introduces a number of new features to the language and standard library. Notably, when Visual C++ 11 is released, variadic template support, among other features, are missing. Microsoft recently released the Visual C++ Compiler November 2012 CTP which adds the following C++11 features: uniform initialization, initializer lists, variadic templates, function template default arguments, delegating constructors, explicit conversion operators and raw strings. A Channel 9 video lecture by Stephan T. Lavavej covers more details about these newly added features. Variadic template enables C++ programmers to write template class and functions with arbitrary number of types. This article mainly focuses on the variadic template and using that to implement DebugPrint
.
int printf ( const char * format, ... );
C style variadic printf
(and its cousins, like fprintf
and sprintf
) exists since the invention of C language in 1970s, is inherently unsafe. The type specified in the format string, can be mismatched by the actual data type of the argument given to printf
.
int n = -1; printf("%u", n);
Variadic template in C++11 allows template writers to create templates which takes in any number of (different) types; Now we can use this new feature to implement a "safe printf
" function. Variadic template function is usually implemented recursively.
void safe_printf(const char *s);
template<typename T, typename... Args>
void safe_printf(const char *s, T value, Args... args);
We know Args
is variadic as indicated by the typename... Args
and Args... args
. Args
can be zero or more types. When it is empty, the base safe_printf
is called. Notice the base safe_printf
is not variadic! Let us see the function body.
void safe_printf(const char *s)
{
while (*s) {
if (*s == '%') {
if (*(s + 1) == '%') {
++s;
}
else {
throw std::runtime_error("invalid format string: missing arguments");
}
}
std::cout << *s++;
}
}
template<typename T, typename... Args>
void safe_printf(const char *s, T value, Args... args)
{
while (*s) {
if (*s == '%') {
if (*(s + 1) == '%') {
++s;
}
else {
std::cout << value;
safe_printf(s + 1, args...); return;
}
}
std::cout << *s++;
}
throw std::logic_error("extra arguments provided to printf");
}
To clarify a bit, the function is actually not truly recursive. A traditional recursive function will call itself directly or indirectly. When variadic template function calls itself, it is actually calling a different function with the same name but have different number of arguments. This safe_printf
is copied from the wikipedia page. The safe_printf
calls cout
to do its work and therefore can display any arbitrary types as long as the programmer overloads the <<
operator for that type. It throws runtime_error
when there is not enough arguments and it also throws logic_error
when there is extra arguments. Here is how to call it.
safe_printf("Product:%, Qty:%, Price is $%\n ", "Shampoo", 1200, 2.65);
Notice: We do not need to specify the types in the format string: we only use %
as a placeholder. To print %
literally, write %%
to escape it. C++ Programmer can use sizeof...(args)
to find out the number of arguments at compile time. The call sizeof(args)...
expand to call the sizeof
for every argument. User can do this for any function, not just sizeof
.
Note: This article focuses on writing function, not class. To know more about writing variadic template class, please refer to the video link in the related section.
Prior to variadic Debug Print, programmers typically make use of sprintf
to format their text before displaying them with OutputDebugString
.
char buf[50];
sprintf(buf, "Product:%s, Qty:%d, Price is $%f\n ", "Shampoo", 1200, 2.65);
OutputDebugStringA(buf);
A problem with sprintf is the given buffer may not be large enough to hold resultant text. So GNU and BSD came out with asprintf
and vasprintf
as extension of C or POSIX. The functions asprintf
and vasprintf
are analogs of sprintf
and vsprintf
, except that they allocate a string large enough to hold the output including the terminating null byte, and return a pointer to it via the first argument. This pointer should be passed to free
to release the allocated storage when it is no longer needed. Note: These functions are only available on Linux and BSD.
char *pbuf = NULL;
asprintf(&pbuf, "Product:%s, Qty:%d, Price is $%f\n ", "Shampoo", 1200, 2.65);
free(pbuf);
In this section, we will convert the safe_printf
to a version which prints debugging information, using Windows API OutputDebugString
, instead of outputting to console using cout
.
#include <sstream>
#include <Windows.h>
void xsprintf(std::string& result, const char *s)
{
while (*s) {
if (*s == '%') {
if (*(s + 1) == '%') {
++s;
}
else {
throw std::runtime_error("invalid format string: missing arguments");
}
}
result += *s++;
}
}
template<typename T, typename... Args>
void xsprintf(std::string& result, const char *s, T value, Args... args)
{
while (*s) {
if (*s == '%') {
if (*(s + 1) == '%') {
++s;
}
else {
std::stringstream stream;
stream << value;
result += stream.str();
xsprintf(result, s + 1, args...); return;
}
}
result += *s++;
}
throw std::logic_error("extra arguments provided to printf");
}
template<typename... Args>
void DebugPrint(const char *s, Args... args)
{
#ifdef _DEBUG
std::string result = "";
xsprintf(result, s, args...);
OutputDebugStringA(result.c_str());
#endif
}
The problem with recursive method, every function call have to carry stateful information (if any). DebugPrint
has a state information known as the result
; We cannot print the information piece by piece, as with cout
because OutputDebugString
will break them up into new lines in Sysinternals DebugView (not in Visual Studio debugger though). And we do not want its caller to know this implementation detail. Therefore DebugPrint
cannot be recursive so it calls a recursive function, xsprintf
to do its work. xsprintf
makes use of stringstream
to do its work. stringstream
can be used to convert POD types into string
(using <<
) and vice-versa (using >>
).
The way to call DebugPrint
is similar to the previous example.
DebugPrint("Product:%, Qty:%, Price is $%\n ",
"Shampoo", 1200, 2.65);
What if we prefer .NET string
placeholder specifier like {0}
for the 1st argument and {1}
for 2nd argument and so forth? I have done that version as well. In addition, I have make it unicode. But this version will not throw any exceptions when there are missing or extra arguments: the programmer will notice this discrepancy in the output anyway. This is to follow the behavior of original non-variadic DebugPrint
(covered in the next section).
#include <string>
#include <sstream>
#include <Windows.h>
std::wstring Anchor( int i )
{
std::wstringstream stream;
stream << i;
std::wstring str = L"{";
str += stream.str() + L"}";
return str;
}
std::wstring Replace( std::wstring fmtstr, size_t index, const std::wstring& s )
{
size_t pos = 0;
std::wstring anchor = Anchor( index );
while( std::wstring::npos != pos )
{
pos = fmtstr.find( anchor, pos );
if( std::wstring::npos != pos )
{
fmtstr.erase( pos, anchor.size() );
fmtstr.insert( pos, s );
pos += s.size();
}
}
return fmtstr;
}
std::wstring Format( std::wstring fmt, size_t index )
{
return fmt;
}
template<typename T, typename... Args>
std::wstring Format( std::wstring fmt, size_t index, T& t, Args&... args )
{
std::wstringstream stream;
stream << t;
std::wstring result = Replace( fmt, index, stream.str() );
++index;
std::wstring str = Format( result, index, args... );
return str;
}
template<typename... Args>
void DebugPrint(const wchar_t *s, Args... args)
{
#ifdef _DEBUG
std::wstring str = Format(std::wstring(s), 0, args...);
OutputDebugStringW(str.c_str());
#endif
}
DebugPrint
calls the recursive Format
function to do its work. Anchor
function returns the "{x}"
string
based on the index and Replace
will use this anchor to find and replace them with the arguments. .NET custom formatting specifier (like "{0:f2}") are not supported. In other words, user is better off using the previous %
version, as "{x}"
serves no purpose other than slowing down the processing time to find and replace them.
void Print( const wchar_t* fmt, Box D1, Box D2 );
Prior to this, there is a version of DebugPrint
class written in 2007, which makes heavy use of Template Pattern (pattern has nothing to do with C++ templates). This class has overloaded Print
functions which varies from having 0 to 10 Box
arguments to provide a illusion of variadic and is no longer actively maintained. If user needs to print 11 variables, he/she is out of luck. Box
works by providing a overloaded constructor for each POD type and convert the POD variable into a string
.
print(L"{0}{1}", 1, 1.0f);
print(L"{0}{1}", 1.0f, 1.0f);
print(L"{0}{1}", 1, 1);
print(L"{0}{1}", 1.0f, 1);
Let's say we compile the above code once using variadic template version and another using the overloaded version. The variadic template version will instantiate 4 different version (see below) maybe would result in code bloat if there are many different combinations used (at least in theory). However, generally, variadic template code is found to have less binary size and faster compilation times.
void print(wstring, int, float);
void print(wstring, float, float);
void print(wstring, int, int);
void print(wstring, float, int);
For the overload version, the same overloaded function is called (see below) for 4 times. Another advantage is the overloaded version can be used with older compiler if the option to use latest C++11 compiler is not available.
void print(wstring, box, box);
To use the new C++11 features provided by the Visual C++ Compiler November CTP, the user have to manually select the compiler in the project configuration, under General. Please note: 'Microsoft Visual C++ Compiler Nov 2012 CTP' is for testing purposes only.
We have seen that variadic template function is usually implemented recursively (though not true recursion as mentioned previously). The difference between overloaded functions and variadic template is mentioned. Readers are strongly encouraged to download and examine the source code.
As mentioned above, if the reader is interested to find out more about variadic templates, especially for variadic template class and how std::tuple
is implemented, I would highly recommend to watch this informative video.
I would humbly recommend anyone to visit the link below, if he/she is keen on a C++11 variadic template library to read and write files.
- 2012-12-30: Included a short
asprintf
paragraph - 2012-12-23: Initial release