char* szName="Britney Spears";
int nAge= 28; // I'm not kidding, born in 1981
CString s;
s.Format( "Name: %s Age: %d", szName, nAge );
But looking at the CString member functions, you might notice another function,
FormatV(), that provides an additional capability: A way to intercept the action before the formating starts. Here's a function I've used to simplify a task of generating XML. Normally, the Attributes of an XML tag need to be surrounded in quotes. So, using CString::Format(), I might use:
CString sXml;
sXml.Format("<Product color=\"%s\">%s</Product>", sAttrColor, sElemValue );
The escaped quote marks (
\") make this line awkward to type and hard to read. So, I wrote an XML-formatting function that would let me use
%q to mean "
replace with a string surrounded by quotes". Here's the function:
CString XmlPrintf( LPCSTR szFmt,... )
{
CString sTxt;
va_list args; va_start(args, szFmt);
CString sFmt= szFmt;
sFmt.Replace("%q","\"%s\"" );
sTxt.FormatV( sFmt, args );
return( sTxt );
}
Note the special use of the
ellipsis (...) in the function declaration. That signals that the function will receive a variable number of arguments. It
must receive at least one,
szFmt, but after that, it's anyone's guess. Example of usage:
m_sOut += XmlPrintf( "<Product size=%q color=%q>%s</Product>",
rc.sProdSize,
rc.sProdColor=="" ? "None" : rc.sProdColor,
rc.sProdName
);
And the output would be, for instance:
int GetAverage( int nVal, ... )
{
va_list pVarArg;
va_start( pVarArg, nVal );
int nCur= nVal;
int nSum=0;
int nArgCnt=0;
while ( nCur != -1 ) {
nSum += nCur;
nArgCnt++;
nCur= va_arg( pVarArg, int );
}
if ( nArgCnt==0 ) { // avoid division by 0
nArgCnt= 1;
}
return( nSum / nArgCnt );
}
In
lines 3-4 we set up to access the argument list. The second parameter to
va_start() is the name of the function argument that will be the first one to use in the following loop. In the loop, we check for the terminating value (-1) and if it's not there, then we accumulate a sum and increment the count of arguments processed. The average is calculated at the end (making sure not to divide by 0).
CString s= XmlElemWithAttrs(
"Product", "Widget", 4,
"size", (LPCSTR)rc.m_sSize, // #1 e.g., "large"
"color", (LPCSTR)rc.m_sColor, // #2 e.g., "blue",
"userData", (LPCSTR)rc.m_sOtherData, // #3 e.g., "",
"rating", (LPCSTR)rc.m_sRating, // #4 e.g., "7"
);
The third parameter is
4, indicating that four name/value pairs will follow. The output of that function call would be something like:
CString XmlElemWithAttrs( LPCSTR szTagName, LPCSTR szTagVal, int nAttrCnt, ... )
{
va_list pVarArg;
CString sRet, sClose;
sRet.Format("<%s ", szTagName );
sClose.Format("</%s>", szTagName );
CString sAttrName, sAttrVal;
va_start( pVarArg, nAttrCnt );
for ( int j=0; j<nAttrCnt; j++ ) {
try {
sAttrName= va_arg( pVarArg, LPCSTR);
sAttrVal= va_arg( pVarArg, LPCSTR);
}
catch( ... ) {
// LogErr("bad args in XmlElemWithAttrs" );
ASSERT(0); // catch during debug runs
sAttrName= sAttrVal="";
}
if ( sAttrVal > "" ) {
sRet += sAttrName + "=\"";
sRet += sAttrVal + "\" ";
}
}
if ( CString(szTagVal) > "" ) { // lazy check for both NULL and ""
sRet += ">";
sRet += szTagVal;
sRet += sClose; // e.g., "</Element>";
}
else {
sRet += "/>"; // e.g., "<Element .../>";
}
return( sRet );
}
Notice that there are three required parameters. The first two are the XML tag name and the element value. The third parameter is the key to the handling of the argument list: It indicates how many attribute name/value pairs will follow.
Line 11 uses that value as the loop counter.
Lines 13-14 break out an attribute name and an attribute value into C-style string pointers which are used in generating the output.
Have a question about something in this article? You can receive help directly from the article author. Sign up for a free trial to get started.
Comments (1)
Commented:
great article, but I would like to mention two points:
1. I think it's probably important to mention how similar types of different size are used. I.e. think about using printf with %f works for both float and double, how is this possible?
The interesting fact is that C/C++ automatically promotes some types to larger types for arguments passed to a function with a varibale argument list, i.e. see http://en.cppreference.com/w/cpp/language/variadic_arguments:
2. It would be a great idea to update this article (or to write a new one) about using varidic templates instead of functions with variable argument list. They are quite powerful and even type safe, i.e.:
Open in new window
Output is:Open in new window
IMO it's highly recommended to use those variadic templates instead of functions with variable argument lists which are quite error prone.