- Feature Articles
- CodeSOD
- Error'd
- Forums
-
Other Articles
- Random Article
- Other Series
- Alex's Soapbox
- Announcements
- Best of…
- Best of Email
- Best of the Sidebar
- Bring Your Own Code
- Coded Smorgasbord
- Mandatory Fun Day
- Off Topic
- Representative Line
- News Roundup
- Editor's Soapbox
- Software on the Rocks
- Souvenir Potpourri
- Sponsor Post
- Tales from the Interview
- The Daily WTF: Live
- Virtudyne
Admin
I unironically say that things like this are why I love C++.
It brought a smile to my weary face. And at my age that's hard to do.
Admin
Fwiw, multiple declarations are fine, so long as they're compatible. Multiple definitions aren't. Admittedly, this is something of a moot point since headers frequently contain type definitions, but:
Admin
In a .cpp file, this is actually a common practice, and has been for 40 years...
ThingsIWant.h #include <IDontWantThese.h>
Bow there are things we want, and things we dont want... but we do not want to change 3rd part headers... so #defining the include guard for the nested header is an accepted way of doing things... Alas the introduction of #pragma once complicates matters..
Note that with "include guards" the entire file needs to be lexically analyzed every pass to find the matching #endif [which can actually change from compile to compile]....
Admin
No, they can only be defined once... per translation unit (TU). In C++, ODR lets the linker throw away any redefinitions between TUs.
Admin
Also, the code swallows NULL pointers to turn them into empty strings.
So, what to do when therethe need arises someday to actually tell apart the NULL case from the rest?
I know the enterprise solution: copy & paste
ToString()
into a newToString2()
function and make it return NULL in theelse
logical branch.Admin
Okay, as usual, TRWTF is the moderation system. Has there been any serious talk about changing it?
Admin
<irony>A post about weird code - clearly a comment rant about how PHP language is evil is missing. Oh, wait... (second check) Yep, it's C++. Forget it.</irony>
Admin
I think any and every C++ developer has worked with a home-grown "stringtools" library/utility class..
Admin
Pretty much every modern compiler these days supports
#pragma once
, so unless you're on some etoteric, ancient platform, it's generally safe to use.Compilers also generally have an optimization built into them to detect header guards and do the same
#pragma once
optimization, so they don't have to fully parse & preprocess the header the second time through. There's no need to worry about whether to use classic header guards or the pragma in terms of compile times.Admin
I don't think that's the same. If
a.h
includesb.h
, I can imaginec.h
wanting to omitb.h
while includinga.h
. This is more likea.c
not only not includinga.h
, but going out of its way to not includea.h
.Admin
Specifically, it needs to be lexically analysed by the preprocessor, and therefore the text inside the ifndef/endif must contain valid preprocessor symbols. Notably, that means that all use of apostrophes and double-quotes must be compatible with their use by C/C++, so you can't do this:
Admin
Technically in all languages types can only be declared once ;-) And even worse, if declarations don't exactly match definitions than even weirder things can happen.
Admin
Where the real fun happens is when you have 2 classes with the same name but different implementations in different translation units, but then link them together in an executable.
I spent days debugging a problem that was caused by a 'private' class declared/defined entirely within a .cpp file that had the same name as a public one in another library it was linking against (though that library was not used in this particular TU). At link time the compiler picked the public one's implementation, except for the destructor, where it picked the private classes implementation. No diagnostics, just crashes at runtime. Easy fix using an anonymous namespace, but it was frustrating as hell to track down.
Admin
On a project I worked on in the mid 90's, somebody got confused and put the definition in the header file and the declaration in some of the C files. The header contained something like
And several C files contained
It all compiled fine but as far as we could tell, the linker used some non deterministic algorithm to decide which of the many definitions amongst all the object files would be the one referenced in the code. The decision would be made on a compilation unit by compilation unit basis and each compilation unit wouldn't necessarily use its own definition.
Admin
Types can be redeclared in C, so long as they aren't redefined. This is perfectly legal:
Admin
I think what happened here is that somebody wanted to override the standard behavior and keep the standard version from being compiled at all.
Admin
"We check if a precompiler definition has been given, [...]" - a what? Shouldn't that be a preprocessor definition?
Admin
That is not a redeckaration because as you said it's the same type. It's just a redundant one ;-)
Admin
But a lot of C code is on esoteric ancient platforms. Often the maintainers of the code would love to move to something new, but have blocker issues in the build tooling that prevent it.
Admin
I actually don't know a single system operating these days that doesn't have C/C++ programs running. Basically every low level component is written in those languages and that hasn't changed over the last three decades.
Admin
I'm confused. Where in the second example is the include guard? Doesn't it need some #ifndef and #endif to actually, you know, guard anything? What am I missing?
Admin
Technically, it's multiple definitions within a translation unit that's problematic.
One could put
in a header, and that can produce hundreds or definitions. They're just all in different translation units.
(Proper optimization will elide them all, but in a debug build they're often all different.)
Admin
might want to take a look at https://www.redox-os.org/ then ;)
Admin
From context of the article: The
#define STRINGTOOLS_H_
causes#include "stringtools.h"
to effectively do nothing, assuming thatstringtools.h
follows the convention exactly.Other commenters mentioned, that this pattern might be used with third-party libraries to include only a subset of its capabilities, e.g.
However, the code in the example would be the equivalent of
which you'd expect to cause the
#include
to do nothing. It only makes sense, ifnul.h
does something before it's guard pattern, which in itself probably is a small WTF.My best guess:
stringtools.h
#include
s its dependencies before the guard, and this code wants to load the same dependencies.Admin
I'm used to a variation on the pattern (which is more effective from a performance perspective and would also I think dodge the issue here): the header file just defines the guard, and the source files are responsible for checking it. Then, if it's already been included, it isn't even loaded, let alone processed by the preprocessor.