Reputation: 715
How does a variable arguement function take object to struct/class? For example :
CString a_csName;
CString a_csAge(_T("20"));
a_csName.Format(_T("My Age is : %s"),a_csAge);
Here CString::Format
is a printf-style variable arguement function which takes this CString
object. How is this possible?
Upvotes: 2
Views: 616
Reputation: 18429
Let me start answering this one differently.
struct Value
{
int nValue;
float fOtherValue;
};
int main()
{
Value theValue;
theValue.nValue = 220;
theValue.fOtherValue = 3.14f;
printf("Value: %d", theValue);
}
This code will print 220
without any issues. But if you pass second argument after theValue
, it wont:
printf("Values: %d, %f", theValue, theValue.fOtherValue);
Since the first variable argument theValue
doesn't meet the size specified by %d
argument. Hence, 3.14
wont (may not) be displayed. Let's not talk how printf
, argument pushing on stack, va_start
and similar things work.
Similarly, when we design a String
class this way:
struct String
{
char* pData;
public:
String()
{
pData = new char[40];
strcpy_s(pData, 40, "Sample");
}
};
And use it this way:
String str;
printf("%s", str);
It would definitely work.
But what about argument and call-stack corruption? What if size of a class (like Value
) is more than the data size (4 for Value
) as specified by format (%d
). Well, in that case, the class designed need to ensure that size of given class is same as argument-size being used by printf
function.
I wont mention the details about it, but CString
uses internal class CStringData
. The latter class holds the string, length, reference count etc. CString
(actually CSimpleStringT
) uses this class, but hold only the pointer of char
/wchar_t
, managed by CStringData
.
Largely, CString
is implemented as the String
class mentioned (as far as data and sizeof
goes).
Upvotes: 0
Reputation: 9189
From: http://msdn.microsoft.com/en-us/library/aa300688%28v=vs.60%29.aspx
- You can freely substitute CString objects for const char* and LPCTSTR function arguments.
Probably CString
is a class without vtable, and with only a attribute of type char*
. This means that sizeof(CString) == sizeof(const char*)
and that if you reinterpret_cast a CString
to a const char*
you will get a working const char*
.
The compiler should not accept passing a struct to the variadic part of the function. But if I am not wrong, in GCC, when you pass a struct to as variadic argument, it's memory is copied (i.e. no copy constructor is used) and a warning is issued. I guess the MSVC is doing the same there, and then, the Format
method is just assuming the given data is a const char*
.
Let me give you a alternative example. Suppose you have a class without vtable which only members is a int. If you pass it to a variadic argument, the compiler would copy this objects contents, which is only an int. On the function implementation, you (somehow) know that you received an int
. Then you query the parameter asking for an int
. Since, at memory level, your class is nothing different than a int
, things will work "fine". The function would access the int
attribute of the class.
Upvotes: 4
Reputation: 21
I recently had to clear this Lint error and stumbled on this thread.
Although it's an interesting investigation into the cast that is occurring, the simplest way to avoid the Lint warning is to pass the CString pointer and not the CString structure by using the CString method Get.String()
as in the following
a_csName.Format(_T("My Age is : %d"), a_csAge.GetString());
Upvotes: 2
Reputation: 715
After a bit of researching and debugging through MFC code found the below, hope it helps whoever faces the ever famous static code analyser error "Passing struct 'CStringT' to ellipsis" which actually is the source of my doubt as well.
http://www.gimpel.com/html/bugs/bug437.htm
Format function being a variadic function, depends on the format specifier present in the first parameter. First parameter is always a char *.
It parses for the format specifier(%s,%d,%i…) and read the var_arg array based on the index of the format specifier found and does a direct cast to char * if %s or int if %d is specified.
So if a CString is specified and corresponding format specifier is %s, then a direct cast attempt to char * is made on the CString object.
CString a_csName;
CString a_csAge(_T("20"));
a_csName.Format(_T("My Age is : %s"),a_csAge);
wcout<<a_csName;
Would print My Age is 20
CString a_csName;
CString a_csAge(_T("20"));
a_csName.Format(_T("My Age is : %d"),a_csAge);
wcout<<a_csName;
Would print My Age is 052134
So there is no intelligence behind this. Just a direct cast. Hence we pass a POD or User-Defined Data structure it doesn’t make a difference.
Upvotes: 4