• (cs) in reply to Petli
    The only wtf is those poor souls who can't understand the concept.

    Agreed. The submitter must be a Java programmer. There's nothing wrong with this. I know of enough places where it's used (usually the last var is a zero-length array, in C99 IIRC you can declare this by creating an array without specifying a length at all (ie char *something[])).

  • (cs) in reply to Georges
    For someone being so smug you sure don't have the intelligence to find the RSS link at the bottom of the main page.

    Yes, maybe guilty of posting after a few beers, point is still valid though. More quality less quantity please.

    One of the front page articles has mentioned that Alex thinks this is taking too much of his time, so he should not be under such pressure to release articles.

  • dkf (unregistered) in reply to Fred
    Fred:
    Showing this code:
    float SquareRootFloat(float number) {
        long i;
        float x, y;
        const float f = 1.5F;
    
    x = number * 0.5F;
    y  = number;
    i  = * ( long * ) &y;
    i  = 0x5f3759df - ( i >> 1 );
    y  = * ( float * ) &i;
    y  = y * ( f - ( x * y * y ) );
    y  = y * ( f - ( x * y * y ) );
    return number * y;
    

    }

    as a WTF?
    It's not a WTF, but it is very tricky and I'm not at all sure that it's worthwhile on modern CPUs (I'd need to measure to work that out.)

    What does it do? Simple. It first approximates (to about the correct order of magnitude) what the solution is using that amazing hack (which reinterprets the float bits as an int, subtracts from a magic number, and reinterprets back as a float); it's very tricky and depends on exactly how IEEE floats are arranged, but it does work (the float is a bit off from something that would just manipulate the mantissa because that seems to give better results for reasons that nobody seems to know!) Then, it does a quick round of Newton-Raphson to get the mantissa about right; luckily, this is a function for which it converges quickly, so one round does a creditable job. If you wanted scientific accuracy, you'd do a few more rounds of N-R, but it converges so fast that you don't have to do many, and as this is presumably part of some code that is critically dependant on being fast (graphics engine or physics engine) speed is better than accuracy.

    I only wish I was good enough at IEEE math to be able to say that I worked this out myself; I had help (from someone who was very impressed by the hackiness).

  • dkf (unregistered) in reply to chrismcb
    chrismcb:
    I have to say, if this struct ONLY contains a pointer, and a chunk of memory, there are other better ways to do this. If this struct has other metadata other than a next pointer, then its ok.
    Depends also on what the struct is being used to do. If it's in something complicated, it might be better off the way it is, especially if the code originated in C and was converted to C++. (Why someone bothered to do that is a WTF, but that's unrevealed by this thread.)
  • dkf (unregistered) in reply to JavaGuy
    JavaGuy:
    Even in 1995, you would not do something this ugly to save 8 bytes.
    It depends on how many thousands or millions of the structures are being allocated. If you've got a lot of them, saving those 8 bytes makes a big difference, as does reducing the amount of memory fragmentation. Indeed, even now it is a challenge to keep memory managed in an efficient and quick fashion; C and C++ aren't that good at it unless you do something elaborate on top. I explain: stack space is often much more limited than heap space so you don't want to use local variables and references for everything (passing things larger than a double by value sucks for performance; it's nice to avoid having to do block copies when not needed) and the standard heap allocators are really quite slow (I've found them to be a bottleneck in many programs, and massively so in threaded code). Writing better ones is "interesting"; the best techniques usually involve using extra information about the distribution of structure sizes and the number of things allocated in one thread and freed in another.

    Actually, if you have tens of millions of structures of the same size, it can be worthwhile writing some very fancy custom allocator code to pack them in as tight as possible; not very easy to make portable, but the saving is colossal...

    In any case, depending on your PoV and the competence of the code's original author, WTFs in memory management code are either rare or frequent. Use of malloc() in production in performance-critical threaded code though, that's a prime example and most C programmers don't know it. (It's the same with C++, except there the way of dealing with it - defining a new allocator core for new IIRC, though I don't remember how - is different.)

  • dkf (unregistered) in reply to sol
    sol:
    mi.size = sizeof(mi); //seems to be what ms thinks is good form
    And it is, since it makes it simple to support different binary versions of the API without requiring all user code to be recompiled. This is a very good thing, but too many programmers (including many who write OSS libraries) don't do this (or the logical equivalent, putting a structure version number in) and it causes much pain.
  • Emanuele (unregistered)

    Damn I thought i knew C quite good till this example. I've used tons of this struct but I never asked myself how these were defined...

    Definetly not a WTF in C but a WTF in C++ (there you should use std::vector<>)...

    Good Job to have this post, Cheers, Ema! :-)

  • silly (unregistered) in reply to dkf

    Actually the code isn't correct ISO-C.

    i = * ( long * ) &y;

    y  = * ( float * ) &i;
    

    Casting around pointers like this and then referencing them violates type aliasing and requires options such as -fno-strict-aliasing in gcc. With the default options you risk miscompilation! Newer gccs would also warn.

    These options typically tends to generate much worse code because the optimizer cannot make many assumptions.

    The only standard blessed way to reference a variable with a different type is to use it as char * (with memcpy() etc.)

  • (cs) in reply to James
    James:
    drfloyd5:
    I can lurk no longer. I made an account just to make this post in person.

    The CodeSOD now officially blows. I will use this 1 WTF to prove it.

    1. The WTF isn't.
    2. There are 30 posts explaining in non-unique ways why #1 is true.

    The signal to noise ratio of the CodeSOD WTFs is pretty crappy. And the comments s/n ratio is even worse. So all in all the whole CodeSOD is a useless read.

    I beg to differ -- I cut my teeth on C++ back in ~1997, and never learned "correct" C. If this apparently hackish attempt at fooling the compiler is the "right" way to do variable-sized structs, maybe there shouldn't be such things (in C anyway) as variable-sized structs. In a vacuum, I still consider this example a WTF, simply because any code that does non-obvious things and isn't commented accordingly is a WTF, at least if the reader doesn't know better.

    I'm glad this got posted, because from time to time I have to get my hands dirty with somebody else's C code, and where I would previously have recoiled in horror at seeing this abuse of assumptions, now I can understand that at least there's precedent -- maybe even a good reason.

    One of the main reasons I read WTF is to learn more about the "right" way of doing things in different languages (usually by watching reaction to the "wrong" way). I want to see the CodeSOD stick around!

    CAPTCHA: "ewww" Heh. Apropos!

    I like the CSOD, I just wish the quality was higher. It would also be cool if there was a CleverCode section as well, where things that look like WTFs but are not could be posted.

  • (cs) in reply to Fellow Lurker
    Fellow Lurker:
    I also can lurk no longer. More quality less quantity. I am going back to reading the old articles I have missed and hope things are fixed by the time I return.

    Bye bye now. Take your time and don't hurry back.

    Fellow Lurker:
    just let us know when there is something new and we don't have to read low standard articles.

    You don't have to read any article at all. Just don't come here. Go to /. or wherever you like to hang out.

    If you do come here, STFU with all the complaining and whining about the name and the content. I can't speak for everyone else, but I'm sure that others besides myself are tired of hearing from you crybabies.

    Fellow Lurker:
    How about a section like - you might think this is a WTF but its actually quite clever.

    How about a section called "Loser's List"? We could make you the first entry.

  • Pete Kirkham (unregistered) in reply to Hallvard
    Hallvard:
    Pete Kirkham:
    Unlike C, in C++ there is no requirement that members of classes are contiguous(...)
    Nonsense. In both languages, members are required to have proper alignment just like other data. Which means the standard can _not_ require them to be contiguous.
    Sorry, I didn't word that at all well. In C++ there is no requirement that the memory allocated for the members of an object is in a contiguous block. There is also, as you state, no requirement in C or C++ that the members be placed consecutively in the memory allocated for them - padding is used between members for alignment reasons. AFAIK, only the managed C++ compilers don't place the object's members in contiguous blocks.
    Hallvard:
    With a compiler where you know the layout, using a reinterpret cast or operator new on some memory you've allocated yourself to create a pointer to this type is safe. But not portable.
    Safe and portable, as long as you don't depend on more layout than the standard requires, and use code which doesn't allow the compiler to "know" that there is just one member.
    It would break in a compiler in which &myNext > &myOrd, as say myOrd[11] might overwrite myNext. As there is no requirement that the parts of a C++ object be in contiguous memory, such a compiler would be legal.
    I'd imagine the length of p->myOrd is given by (reinterpret_cast<void*>(p->myNext)-reinterpret_cast<void*>(p->myOrd)-sizeof(summand*))/sizeof(OrdArith::OrdElem ), or if the sizes of the types are the same: (p-p->next-1).
    No. The compiler - and sizeof()- doesn't know that the programmer uses another size than the structure definition imples. The programmer has to take care of the size himself.
    Assuming that a contiguous block of memory starting at 'p' is used for the summand data, then typically you'd place one summand at p, and fill ((summand*)p)->myOrd with N OrdArith::OrdElem objects. This takes N*sizeof(OrdArith::OrdElem). Then you put the next summand immediately after it, at (void*)p + sizeof(p) + N*sizeof(OrdArith::OrdElem). (assuming p and OrdElem have the same alignment). Hence p->myNext = p + sizeof(p) + N*sizeof(OrdArith::OrdElem) => N = ((void*)p->myNext - (void*)p - sizeof(p))/sizeof(OrdArith::OrdElem). If sizeof(OrdArith::OrdElem) == sizeof(p), then that simplifies to N = p->myNext - (p + 1). In my haste, I got the sign wrong above, (and also made a typo on 'public',) and didn't bother with as full an explanation. Someone asked why there wasn't a length member in summand, I gave an answer why it wasn't required in a typical use case. I didn't go into full detail as I imagined anyone else would be able to work it out themselves.

    By 'the sizes of the types' I was referring to sizeof(OrdArith::OrdElem) and sizeof(p), not the length of the myOrd array, and how to get the length of the array from these sizes and the data in the struct. Since I stated this length was dependent on the values in the structure at runtime, I agree that the it isn't known at compile time, that using sizeof will not return the array length, and that the programmer has to take care of the size himself, probably using something like the method above. Sorry that wasn't clear in your reading of my post; there's a limit to how much detail it's worth putting into a comment on a humorous forum.

  • AdT (unregistered) in reply to darin
    darin:
    Accurate square-root computation can be expensive in hardware, but relatively simple in software. Many RISC computers don't have "fsqrt" since the instruction will take longer to execute than other floating point instructions. Software can converge on the answer in only a few iterations.

    That's why most modern CPUs use their own software, called microcode.

  • Maciej (unregistered) in reply to Emanuele
    Emanuele:
    Definetly not a WTF in C but a WTF in C++ (there you should use std::vector<>)...

    I'm not sure that this construct and std::vector<> solve the same problem. There are certainly more "clean" ways to handle variable-length lists as members of a structure, even in C. The reason for this particular way of doing it is to make sure that the variable length data is contiguous with the header structure. There are a number of reasons for doing this, from concerns about memory fragmentation to convenience when dealing with input data. Using an std::vector<> doesn't do that.

  • (cs) in reply to KenW
    Fellow Lurker:
    I also can lurk no longer. More quality less quantity. I am going back to reading the old articles I have missed and hope things are fixed by the time I return.
    

    Bye bye now. Take your time and don't hurry back.

    Fellow Lurker:
    
    just let us know when there is something new and we don't have to read low standard articles.
    

    You don't have to read any article at all. Just don't come here. Go to /. or wherever you like to hang out.

    If you do come here, STFU with all the complaining and whining about the name and the content. I can't speak for everyone else, but I'm sure that others besides myself are tired of hearing from you crybabies.

    Fellow Lurker:
    
    How about a section like - you might think this is a WTF but its actually quite clever.
    

    How about a section called "Loser's List"? We could make you the first entry.

    Haha I love it, KenW-ischenko was rude to me

    Briliant

    Sincerely,

    UnresolvedExternal

  • Emanuele (unregistered) in reply to Maciej
    Maciej:
    Emanuele:
    Definetly not a WTF in C but a WTF in C++ (there you should use std::vector<>)...

    I'm not sure that this construct and std::vector<> solve the same problem. There are certainly more "clean" ways to handle variable-length lists as members of a structure, even in C. The reason for this particular way of doing it is to make sure that the variable length data is contiguous with the header structure. There are a number of reasons for doing this, from concerns about memory fragmentation to convenience when dealing with input data. Using an std::vector<> doesn't do that.

    Yeah I agree concerning the C part of "the variable length data is contiguous with the header structure". But in C++ the type used could not be a primitive type at all, so you have to use appropriate constructors (like new) and/or portable containers as std::vector<> . You miss that data is contiguous but hey this is C++ (C++ struct/classes/types aren't just POD as in C).

    Cheers, Emanuele.

  • (cs) in reply to Pete Kirkham
    Pete Kirkham:
    There's a clue for you in the non-C pubic keyword, meaning it has to be compiled with a C++ compiler, and will be treated as a class with all members public. Unlike C, in C++ there is no requirement that members of classes are contiguous

    Actually there is; if the class is a "Plain Old Data" struct then it basically has to behave like a C struct, including that the members must occur in the order given and there is no 'hidden data'. Informally, a POD-struct is a struct (or class) that looks like a C one! In this case the public: modifier makes no difference. This struct would be POD if and only iff that OrdElem thing is also a POD (e.g. a built-in type, or another simple struct).

  • (cs) in reply to eraserhd
    eraserhd:
    This isn't a WTF. In fact, look at the implementation of any STL std::string class in C++.

    From GNU std::string:

          struct _Rep_base
          {
            static size_type _S_empty_rep_storage[];
    

    This does not comply with the C++ standard. It's a safe bet that the GNU C++ compiler implements extensions to support this syntax and semantics.

  • Maciej (unregistered) in reply to Emanuele
    Emanuele:
    Maciej:
    Emanuele:
    Definetly not a WTF in C but a WTF in C++ (there you should use std::vector<>)...

    I'm not sure that this construct and std::vector<> solve the same problem. There are certainly more "clean" ways to handle variable-length lists as members of a structure, even in C. The reason for this particular way of doing it is to make sure that the variable length data is contiguous with the header structure. There are a number of reasons for doing this, from concerns about memory fragmentation to convenience when dealing with input data. Using an std::vector<> doesn't do that.

    Yeah I agree concerning the C part of "the variable length data is contiguous with the header structure". But in C++ the type used could not be a primitive type at all, so you have to use appropriate constructors (like new) and/or portable containers as std::vector<> . You miss that data is contiguous but hey this is C++ (C++ struct/classes/types aren't just POD as in C).

    Cheers, Emanuele.

    I'd argue that it's still a legitimate way to do things if you know you are dealing with basic types or structs of basic types. If you're using C++, you probably have very little hope of getting fine-grained control over heap fragmentation, but imposing structure on binary blobs (binary files, network packets, etc.) is still useful and quick with a method like this.

    Maciej

  • Andy (unregistered) in reply to John
    John:
    Yeah, that's not too bad. It can be nice to just have an array inside the structure, so you just have to malloc or free one thing at a time (and increase space efficiency too, although I doubt that that would matter normally). Of course, then you have to start using "malloc" instead of "new" in C++ code, and worry about lower-level things than you otherwise would have to. In any case, I know that the linux kernel uses this technique.

    Sometimes this makes sense. I doubt this is a good idea in C++ though. But the real WTF is to declare a struct explicit as public as structs are public by default.

  • Blindy (unregistered)

    This isn't a WTF, in fact it's so standard the very operating system you're most likely reading this on is using it. Excerpt from the Win32 API:

    typedef struct tagBITMAPINFO { BITMAPINFOHEADER bmiHeader; RGBQUAD bmiColors[1]; } BITMAPINFO, *PBITMAPINFO;

    http://msdn.microsoft.com/en-us/library/dd183375(v=VS.85).aspx

  • sad man (unregistered)

    Maybe it was standard 20 o 30 years ago. And surely it appears in linux kernels. It wouldn't be a problem if there weren't other possibilities to have a variable sized list of things to sum over. But, my friend... It's not portable. It's not safe. At the very least change made not only in the structure, but in its environment, all the thing will crash. And, ey, linux kernel crashes are not such a rare thing. How about a linked list? How an explicit memory buffer to ask memory chunks? How a little bit of design? The real WTF is the fact that so much people don't ever recognize this as a complete failure of programming and engineering even after presented as such, with annotations. At least, the original code writer was aware of what he was doing.

  • SomeName (unregistered)

    I have seen (and then used) similar technique. Except it was multiple arrays and structure itself only contained capacity for each. Luckily it was C++ so getting pointer and bounds checking was error-free.

Leave a comment on “Must Be Last”

Log In or post as a guest

Replying to comment #:

« Return to Article