• LZ79LRU (unregistered)

    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.

  • Naomi (unregistered)

    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:

    naomi@Melchior:~$ gcc -x c -
    #include "stdio.h"
    // Declaration
    void foo();
    struct bar;
    // Another declaration
    void foo();
    struct bar;
    // Declaration and definition
    void foo() {
      printf("hello world\n");
    }
    struct bar { int baz; };
    int main() {
      foo();
      return 0;
    }
    naomi@Melchior:~$ ./a.out 
    hello world
    
  • TheCPUWizard (unregistered)

    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]....

  • Tim (unregistered)

    But things can only get declared once

    No, they can only be defined once... per translation unit (TU). In C++, ODR lets the linker throw away any redefinitions between TUs.

  • Sauron (unregistered)

    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 new ToString2() function and make it return NULL in the else logical branch.

  • Naomi (unregistered)

    Okay, as usual, TRWTF is the moderation system. Has there been any serious talk about changing it?

  • dusoft (unregistered)

    <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>

  • k5.user (unregistered)

    I think any and every C++ developer has worked with a home-grown "stringtools" library/utility class..

  • Anonymous') OR 1=1; DROP TABLE wtf; -- (unregistered)

    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.

  • Naomi (unregistered)

    In a .cpp file, this is actually a common practice, and has been for 40 years...

    I don't think that's the same. If a.h includes b.h, I can imagine c.h wanting to omit b.h while including a.h. This is more like a.c not only not including a.h, but going out of its way to not include a.h.

  • (nodebb) in reply to TheCPUWizard

    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]....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]....

    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:

    #ifdef SOMETHING_THAT_IS_NOT_DEFINED
    We can't use this stuff.
    #endif
    
  • (nodebb)

    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.

  • Splatmanedeux (unregistered) in reply to Tim

    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.

  • (nodebb)

    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

    int importantGlobalDefinition = 1;
    

    And several C files contained

    extern int  importantGlobalDefinition;
    

    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.

  • Naomi (unregistered) in reply to MaxiTB

    Technically in all languages types can only be declared once ;-)

    Types can be redeclared in C, so long as they aren't redefined. This is perfectly legal:

    struct foo;
    struct foo;
    
  • (nodebb)

    I think what happened here is that somebody wanted to override the standard behavior and keep the standard version from being compiled at all.

  • Officer Johnny Holzkopf (unregistered)

    "We check if a precompiler definition has been given, [...]" - a what? Shouldn't that be a preprocessor definition?

  • MaxiTB (unregistered) in reply to Naomi

    That is not a redeckaration because as you said it's the same type. It's just a redundant one ;-)

  • (nodebb) in reply to Anonymous') OR 1=1; DROP TABLE wtf; --

    unless you're on some etoteric, ancient platform

    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.

  • (nodebb) in reply to dkf

    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.

  • FrodoB (unregistered)

    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?

  • oh no (unregistered) in reply to Naomi

    Technically, it's multiple definitions within a translation unit that's problematic.

    One could put

    static void foo() { return; }
    

    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.)

  • holy shit now I'm actually commenting here... (unregistered) in reply to MaxiTB

    might want to take a look at https://www.redox-os.org/ then ;)

  • (nodebb) in reply to FrodoB

    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?

    From context of the article: The #define STRINGTOOLS_H_ causes #include "stringtools.h" to effectively do nothing, assuming that stringtools.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.

    // Include library, but omit its internal utilities, that have a name clash with our code base.
    #define NUL_UTIL_H___
    #include "nul.h"
    

    However, the code in the example would be the equivalent of

    #define NUL_H___
    #include "nul.h"
    

    which you'd expect to cause the #include to do nothing. It only makes sense, if nul.h does something before it's guard pattern, which in itself probably is a small WTF.

    My best guess: stringtools.h #includes its dependencies before the guard, and this code wants to load the same dependencies.

  • Craig (unregistered)

    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.

Leave a comment on “Include This”

Log In or post as a guest

Replying to comment #:

« Return to Article