Just another WordPress.com site

Main menu

Post navigation

[cppx] Is C4099 really a sillywarning? (MSVC sillywarnings)

Evidently some people get to my blog by googling up C4099, the MSVC warning that you’ve used struct in one place and class in another place, for the same class. This is one of the alleged sillywarnings in my sillywarnings suppression header. But given e.g. the discussion at StackOverflow, is it really a sillywarning, or perhaps something to take seriously?

In standard C++ a forward declaration using struct is equivalent to one using class, there is no difference. When you define a class there is a difference in the default accessibility of base classes and members, but there’s no difference for a forward declaration. The C++98 standard is perhaps not overly clear on this, but it’s clear enough, in §7.1.5.3/3 (emphasis added):

The class-key or enum keyword present in the elaborated-type-specifier shall agree in kind with the declaration to which the name in the elaborated-type-specifier refers. This rule applies to the form of elaborated-type-specifier that declares a class-name or friend class since it can be construed as referring to the definition of the class. Thus, in any elaborated-type-specifier, the enum keyword shall be used to refer to an enumeration (7.2), the union class-key shall be used to refer to a union (clause 9), and either the class or struct class-key shall be used to refer to a class (clause 9) declared using the class or struct class-key.

Thus, the warning does not tell you that there’s anything wrong with your code.

On the contrary, as I discuss below, if this warning occurs then a potential problem is probably not present in your code! :-) It is IMO a true sillywarning. But this sillywarning is vaguely associated with the fact that MSVC is non-standard in that it does not honor the equivalence of struct and class for a forward-declaration.

With MSVC compiling [foo.cpp] generates an object code file [foo.obj] where each source code name has been extended with some extra text that specifies the type. This is called name mangling or name decoration. In practice all C++ compilers use name mangling to implement cross-module type checking and to support C++ function overloading with old C-oriented linkers, but it is unfortunately not standardized, and Microsoft does not fully document the MSVC name mangling.

You can inspect the decorated names using e.g. the MSVC dumpbin tool. dumpbin “unmangles” the name for you, and you can also use the MSVC undname tool to do that separately. And if you want to do it programmatically then there are a number of APIs for that, but let’s just check what mangled names look like & what they encode:

To the right dumpbin presents first each mangled name, and then an unmangled version (what you’d get from undname). And in the unmangled names you can see that the source code’s use of struct and class is encoded in each mangled name. This is OK for CompleteStruct and CompleteClass, where it helps with enforcing the standard’s One Definition Rule (which requires identical token sequences in two definitions of the same class), but it’s decidedly not OK for ForwardStruct and ForwardClass, where nothing is known about the definitions’ use of struct versus class.

To do this properly the decorated name for a function involving a forward-declared class with unknown definition, would have to use some third code for struct-or-class, which the linker would then have to resolve like a kind of wildcard. An easier way could be to punt on the checking and not encode the use of struct versus class, at all. That’s perhaps not as drastic as it sounds.

Given the MSVC name mangling scheme, perfectly fine, standard C++ code can fail to link. In this case you will generally not get warning C4099, because the problem is due to some declaration using only a forward declaration of a class, with the definition unknown at that point, and thus no detection of inconsistent use of struct and class. Making the definition known is one way to work around this MSVC bug; the code then links fine, but may then produce warning C4099.

I.e., when there is a problem then you do not get the warning, but when everything’s fine you may get it.

Here, perhaps to avoid excessive build times, the programmer has just forward-declared Blah in file [foo.h], but using struct instead of class, which is perfectly fine standard C++ but not so good with MSVC:

Warning C4099 does tell you something, namely that it might be possible to change the code so that it is still standard-conforming but won’t link with MSVC. But then, if you get this warning it may just as likely stem from some header that you have no control over, like a Microsoft header. And if there is an actual problem (for MSVC) then you get a link error and generally no warning.

When I included C4099 in my sillywarnings suppression header it was not because I had ever had this warning generated for my own code. Instead, it had been generated by code that I had no control over. And thus at least for my own coding it was and is just an undesirable sillywarning.

However, that is for a style of programming based on 100% clean builds. In a setting where a build regularly produces a stream of warnings that one ignores, warning C4099 probably won’t distract one from more real warnings. But in such a setting it probably won’t help either (to the degree that it could), since in such a setting warnings are regularly ignored.

Syntax highlighting issue

I’m using WordPress’ [sourcecode] meta-tag for source code syntax coloring and formatting. Source code examples are still presented when you have JavaScript disabled, but then without syntax coloring, and without line wrapping or scrolling.