C preprocessor tricks, tips, and idioms (2015)

(github.com)

78 points | by Pete_D 1701 days ago

2 comments

  • WalterBright 1701 days ago
    I used to use all sorts of preprocessor tricks in my C code. I eventually made an effort to eliminated all preprocessor use in the code (except for #include, where there was no alternative).

    The result was surprisingly pleasing. The code looked much nicer without all the #'s breaking up the indentation, and it made C look like a more modern language :-)

  • sigjuice 1701 days ago
    There are lots of horrific weapons like this to ever torment the universe. e.g. http://p99.gforge.inria.fr/p99-html/index.html
    • coldpie 1701 days ago
      I work in C all day every day. It's fine, I get by. And then I run into macros. Fucking macros.

      Hey guess what REQUEST and REQUEST_FIXED_SIZE do here: https://gitlab.freedesktop.org/xorg/xserver/blob/master/dix/... Did you guess "declares a new variable called 'stuff'" and "returns early", respectively?

      Do you think dixAllocateObjectWithPrivates is a function here? https://gitlab.freedesktop.org/xorg/xserver/blob/master/dix/... Haha, think again! Macro.

      Guess what fbGetPixmapBitsData does here: https://gitlab.freedesktop.org/xorg/xserver/blob/master/fb/f... Did you guess assign values to the latter 3 arguments, two of which aren't even passed by reference??

      I'm picking on the xserver because it's my most memorable encounter with bad macros, but agh. Agh!

      • mehrdadn 1701 days ago
        Honestly that's both poor naming and also abuse of macros combined with poor tooling (lack of syntax highlighting for macro usage for the reader). They do make it easier to shoot yourself in the foot, but they also let you do useful stuff that you simply couldn't do otherwise. The trouble is mostly teaching people to only use them for such purposes and not for anything else.
        • Spivak 1701 days ago
          I think it’s hard to blame any macro system that operates at the source code level for this kind of thing since it’s basically impossible to prevent this type of abuse. The C preprocessor doesn’t enable it any more or less than others.
      • taylodl 1701 days ago
        Where do you work that you get to work in C all day every day? I haven’t been able to do that in over 25 years. Sometimes I miss those days. Then you see one of these stupid macros!
        • waiseristy 1701 days ago
          Come to automotive, you can program in c90 all the live long day
        • coldpie 1699 days ago
          See my profile. We're hiring.
      • kazinator 1701 days ago
        I have a problem with:

          ProcQueryExtension(ClientPtr client)
          {
            xQueryExtensionReply reply;
            int i;
        
            REQUEST(xQueryExtensionReq);
        
            REQUEST_FIXED_SIZE(xQueryExtensionReq, stuff->nbytes);
        
            reply = (xQueryExtensionReply) {
              .type = X_Reply,
              .sequenceNumber = client->sequence,
              .length = 0,
              .major_opcode = 0
            };
        
        
        What's wrong with:

          ProcQueryExtension(ClientPtr client)
          {
            xQueryExtensionReply reply = {
              .type = X_Reply,
              .sequenceNumber = client->sequence,
              .length = 0,
              .major_opcode = 0
            };
            int i;
            REQUEST(xQueryExtensionReq);
            REQUEST_FIXED_SIZE(xQueryExtensionReq, stuff->nbytes);
        
        Can't we define and initialize a structure any more without some damned compound literals?

        > Did you guess assign values to the latter 3 arguments

        In C++, functions do that.

           void assign(int &target, int val) { target = val; }
        
           assign(x, 42);
        • delinka 1701 days ago
          >In C++, functions do that.

          GP means 'they do that unexpectedly.' In your C++ code, your function doesn't just up and decide to make changes to the parameters; `target` is a reference and therefore the caller should expect it to be modified. In GP's case, he's presumably complaining that the macro definition for `fbGetPixmapBitsData` gives no indication that the last three arguments will have their addresses taken to be passed elsewhere.

          • kazinator 1701 days ago
            "In your C code, your macro doesn't just up and decide to make changes to the parameters. TARGET is a macro parameter, and therefore the caller should expect it to be modified."

            Sorry, you don't hold water. A C++ function can be edited from a pure function to one with non-const, mutated ref parameter, and some (perhaps all) uses of that function in the program will still compile. That counts as "just up and decide to make changes to the parameters".

            You have to read the definition to see what is going on, just like with a macro.

            > macro definition for `fbGetPixmapBitsData` gives no indication that the last three arguments will have their addresses taken to be passed elsewhere.

            If there is no indication, then it's not that macro itself that is arranging the mutation syntax. It must be expanding into something that looks innocent. E.g. we can have a macro over my C++ assign function above:

               #define asn(x, y) assign(x, y)
            
            Sure, the macro definition doesn't give a clue because it expands to something that looks like another function or macro call, which we then have to understand. The C++ function is the culprit that is mutating x, not the macro.

            It's true that with macros, we can build up a labyrinth of expansions where such a thing can hide more easily; whereas if we use nothing but C++ functions, the one we are calling has to steal that address, not any of its delegates.

        • mckinney 1701 days ago
          It's difficult to justify the macro aspect of the preprocessor, there are a thousand horror stories for every questionably good macro use-case. The only somewhat respectable macro definition usage I recall from my C++ days was type aliasing to support multi-platform build targets. But even that is hard to rationalize outside the standard libraries.