CString as variable arguments
-
I have a function that uses a variable argument list, but the va_arg macro always throws an exception. Why can't I use CString objects as my arguments? What am I doing wrong? My code is below.
void COdb::WriteAttribute( CStdioFile& fileOutput,
const CString& strTagName,
const CString& strAttribute,
const CString& strValue, ... )
{
va_list vl;
va_start( vl, strValue );CString strBuffer; strBuffer.Format( " <%s %s=\\"%s\\"/>", strTagName, strAttribute, strValue ); CString strOpAttr = va\_arg( vl, CString );
}
-
I have a function that uses a variable argument list, but the va_arg macro always throws an exception. Why can't I use CString objects as my arguments? What am I doing wrong? My code is below.
void COdb::WriteAttribute( CStdioFile& fileOutput,
const CString& strTagName,
const CString& strAttribute,
const CString& strValue, ... )
{
va_list vl;
va_start( vl, strValue );CString strBuffer; strBuffer.Format( " <%s %s=\\"%s\\"/>", strTagName, strAttribute, strValue ); CString strOpAttr = va\_arg( vl, CString );
}
Royce Fickling wrote:
Why can't I use CString objects as my arguments?
Why don't you use
LPCTSTR
as the parameter types instead?void COdb::WriteAttribute( CStdioFile& fileOutput, LPCTSTR strTagName, LPCTSTR strAttribute, LPCTSTR strValue, ... )
{
va_list vl;
va_start( vl, strValue );
CString strBuffer;
strBuffer.Format( " <%s %s=\"%s\"/>", strTagName, strAttribute, strValue);
LPCTSTR strOpAttr = va_arg( vl, LPCTSTR);
}"Love people and use things, not love things and use people." - Unknown
"The brick walls are there for a reason...to stop the people who don't want it badly enough." - Randy Pausch
-
I have a function that uses a variable argument list, but the va_arg macro always throws an exception. Why can't I use CString objects as my arguments? What am I doing wrong? My code is below.
void COdb::WriteAttribute( CStdioFile& fileOutput,
const CString& strTagName,
const CString& strAttribute,
const CString& strValue, ... )
{
va_list vl;
va_start( vl, strValue );CString strBuffer; strBuffer.Format( " <%s %s=\\"%s\\"/>", strTagName, strAttribute, strValue ); CString strOpAttr = va\_arg( vl, CString );
}
Interesting. I looked at this for a while. The problem is the "strValue" argument. The offset of the strValue parameter is used to calculate the offset of the start of the variable-length parameter list. If you don't pass it by reference the function works fine. I can't figure out why the reference doesn't work - the sizeof a reference is the same as the sizeof a CString (4 bytes on my 32-bit build), so I would expect the same result. I guess references are passed on the stack differently. I don't think this old C feature was meant to deal with C++ references anyway... Regardless, passing strValue by value instead of by reference fixes it:
void COdb::WriteAttribute( CStdioFile& fileOutput,
const CString& strTagName,
const CString& strAttribute,
const CString strValue, ... )
...If anyone wants to analyze this further, here's the macro definitions:
#define _ADDRESSOF(v) ( &reinterpret_cast<const char &>(v) )
#define _INTSIZEOF(n) ( (sizeof(n) + sizeof(int) - 1) & ~(sizeof(int) - 1) )#define _crt_va_start(ap,v) ( ap = (va_list)_ADDRESSOF(v) + _INTSIZEOF(v) )
#define _crt_va_arg(ap,t) ( *(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) )
#define _crt_va_end(ap) ( ap = (va_list)0 )*edit* Now that the caffeine has kicked in, I realize taking the address of a reference gives you the address of the object referenced, not the address of the reference. Yeah - those macros weren't meant to work with references
:) MarkMark Salsbery Microsoft MVP - Visual C++ :java:
modified on Friday, August 29, 2008 2:09 PM
-
Interesting. I looked at this for a while. The problem is the "strValue" argument. The offset of the strValue parameter is used to calculate the offset of the start of the variable-length parameter list. If you don't pass it by reference the function works fine. I can't figure out why the reference doesn't work - the sizeof a reference is the same as the sizeof a CString (4 bytes on my 32-bit build), so I would expect the same result. I guess references are passed on the stack differently. I don't think this old C feature was meant to deal with C++ references anyway... Regardless, passing strValue by value instead of by reference fixes it:
void COdb::WriteAttribute( CStdioFile& fileOutput,
const CString& strTagName,
const CString& strAttribute,
const CString strValue, ... )
...If anyone wants to analyze this further, here's the macro definitions:
#define _ADDRESSOF(v) ( &reinterpret_cast<const char &>(v) )
#define _INTSIZEOF(n) ( (sizeof(n) + sizeof(int) - 1) & ~(sizeof(int) - 1) )#define _crt_va_start(ap,v) ( ap = (va_list)_ADDRESSOF(v) + _INTSIZEOF(v) )
#define _crt_va_arg(ap,t) ( *(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) )
#define _crt_va_end(ap) ( ap = (va_list)0 )*edit* Now that the caffeine has kicked in, I realize taking the address of a reference gives you the address of the object referenced, not the address of the reference. Yeah - those macros weren't meant to work with references
:) MarkMark Salsbery Microsoft MVP - Visual C++ :java:
modified on Friday, August 29, 2008 2:09 PM
After converting the CString reference parameters to LPCTSTR parameters, it works, but only when the optional parameters are actually passed. When they are not passed, I still get garbage in the 1st optional parameter. So how do I determine when I have them and when I don't? I thought that if the optional arguments were not passed, the first call to va_arg would return a null, but that is not the case. I have posted my code below.
void COdb::WriteAttribute( CStdioFile& fileOutput,
LPCTSTR strTagName,
LPCTSTR strAttribute,
LPCTSTR strValue, ... )
{
va_list vl;
va_start( vl, strValue );CString strBuffer; strBuffer.Format( " <%s %s=\\"%s\\"/>", strTagName, strAttribute, strValue ); LPCTSTR pstrOpArg = va\_arg( vl, LPCTSTR ); CString strOption; bool bAttr = true; while ( pstrOpArg ) { if ( bAttr ) { strOption.Format( "%s=", pstrOpArg ); strBuffer += strOption; bAttr = false; } else { strOption.Format( "\\"%s\\"", pstrOpArg ); strBuffer += strOption; bAttr = true; } pstrOpArg = va\_arg( vl, LPCTSTR ); } fileOutput.WriteString( strBuffer );
}
-
After converting the CString reference parameters to LPCTSTR parameters, it works, but only when the optional parameters are actually passed. When they are not passed, I still get garbage in the 1st optional parameter. So how do I determine when I have them and when I don't? I thought that if the optional arguments were not passed, the first call to va_arg would return a null, but that is not the case. I have posted my code below.
void COdb::WriteAttribute( CStdioFile& fileOutput,
LPCTSTR strTagName,
LPCTSTR strAttribute,
LPCTSTR strValue, ... )
{
va_list vl;
va_start( vl, strValue );CString strBuffer; strBuffer.Format( " <%s %s=\\"%s\\"/>", strTagName, strAttribute, strValue ); LPCTSTR pstrOpArg = va\_arg( vl, LPCTSTR ); CString strOption; bool bAttr = true; while ( pstrOpArg ) { if ( bAttr ) { strOption.Format( "%s=", pstrOpArg ); strBuffer += strOption; bAttr = false; } else { strOption.Format( "\\"%s\\"", pstrOpArg ); strBuffer += strOption; bAttr = true; } pstrOpArg = va\_arg( vl, LPCTSTR ); } fileOutput.WriteString( strBuffer );
}
Royce Fickling wrote:
I thought that if the optional arguments were not passed, the first call to va_arg would return a null, but that is not the case.
Yes, that is NOT the case. You'd need to pass a NULL parameter.
Mark Salsbery Microsoft MVP - Visual C++ :java: