[HN Gopher] Detecting if an expression is constant in C
       ___________________________________________________________________
        
       Detecting if an expression is constant in C
        
       Author : signa11
       Score  : 37 points
       Date   : 2025-05-09 17:09 UTC (4 days ago)
        
 (HTM) web link (nrk.neocities.org)
 (TXT) w3m dump (nrk.neocities.org)
        
       | wahern wrote:
       | > This works. But both gcc and clang warn about the enum being
       | anonymous... even though that's exactly what I wanted to do. And
       | this cannot be silenced with #pragma since it's a macro, so the
       | warning occurs at the location where the macro is invoked.
       | 
       | You can use _Pragma instead of #pragma. E.g.
       | #define C(x) ( \         _Pragma("clang diagnostic push") \
       | _Pragma("clang diagnostic ignored \"-Wvisibility\"") \
       | (x) + 0*sizeof(void (*)(enum { tmp = (int)(x) })) \
       | _Pragma("clang diagnostic pop") \       )
       | 
       | EDIT: Alas, GCC is a little pickier about where _Pragma is
       | allowed so you may need to use a statement expression. Also, it
       | seems GCC 14 doesn't have a -W switch that will disable the
       | anonymous enum warning.
        
       | pjc50 wrote:
       | It's remarkable that people will say that doing this kind of
       | thing is better than learning a language which actually lets you
       | enforce this with the type system.
       | 
       | (or even just insist that users use the version of the language
       | which supports "constexpr"!)
        
         | oguz-ismail wrote:
         | What language is that? Is it available everywhere (everywhere)
         | C is?
        
           | mitthrowaway2 wrote:
           | Indeed, usually if I'm using C these days it's because I only
           | have access to a c compiler for my target platform, or
           | because I'm modifying an existing C codebase.
        
         | uecker wrote:
         | I do not think anybody said this. The point is that these
         | macros work for early versions of C. If you need to support
         | early versions of C, learning another language is not a
         | solution. If you don't have to, you can use C23's constexpr.
        
         | trealira wrote:
         | C used to seem like a beautiful and simple language to me, but
         | as I used it and learned more about it, it seemed more complex
         | under the surface, and kind of janky as well. It's just
         | utilitarian.
        
         | wat10000 wrote:
         | Learning such a language doesn't mean I can use it.
        
         | o11c wrote:
         | The problem is that no such language exists.
         | 
         | There are many languages that provide _one particular feature_
         | that C doesn 't provide, but they do this at the cost of
         | excluding numerous other features that C widely relies on.
        
         | kjs3 wrote:
         | "I have no idea what problem you're trying to solve, what the
         | constraints are, what the use cases might be, what tools are
         | available on the platform, what the job or regulations require,
         | what the skillsets of the people involved are, what the
         | timeline is...but I'm absolutely, unshakably certain that I
         | have a magic bullet that will make all your problems go away."
         | 
         | FTFY.
        
       | sleirsgoevy wrote:
       | The Linux kernel has even a way to determine whether the
       | expression is compile-time, WITHOUT aborting compilation in
       | either case.
       | 
       | The trick is this (copied vebratim from Linux):
       | 
       | #define __is_constexpr(x) (sizeof(int) == sizeof(*(8 ? ((void
       | *)((long)(x) * 0l)) : (int *)8)))
       | 
       | Explanation: if x is a constant expression, then multiplying it
       | by zero yields a constant 0, and casting a constant 0 to void*
       | makes a null pointer constant. And the ternary expression, if one
       | of its sides is a null pointer constant, collapses to the type of
       | the other side (thus the type of the returned pointer will be
       | int*, and the sizeof will match). And if x was not constant, then
       | the lefthand side would not be considered a null pointer constant
       | by type inference, the type of the ternary expression will be
       | void*, and the sizeof check will not match.
       | 
       | With a few more clever tricks, it's even possible to implement a
       | compile-time "type ternary expression", like this: TYPE_IF(2 * 2
       | == 4, int, long). This is left as an exercise for the reader.
        
         | amelius wrote:
         | This reminds me of the days when Boost was a thing. It was full
         | of tricks like this.
        
           | usrnm wrote:
           | It still is a thing, though.
        
         | cperciva wrote:
         | _With a few more clever tricks..._
         | 
         | I did this with my PARSENUM macro (https://github.com/Tarsnap/l
         | ibcperciva/blob/master/util/pars...) to parse strings into
         | floating-point, unsigned integer, or signed integer types (and
         | check bounds) using a single interface.
        
       | bobbyi wrote:
       | I thought this would work:
       | 
       | #define C(x) (sizeof(char[x]), x)
       | 
       | sizeof is a compile-time operation so x need to be known at
       | compile time.
       | 
       | It didn't work as expected. It turns out there is an exception
       | and the standard says that sizeof is actually calculated at
       | runtime specifically for variable length arrays:
       | 
       | > The sizeof operator yields the size (in bytes) of its operand,
       | which may be an expression or the parenthesized name of a type.
       | The size is determined from the type of the operand. The result
       | is an integer. If the type of the operand is a variable length
       | array type, the operand is evaluated; otherwise, the operand is
       | not evaluated and the result is an integer constant.
        
       ___________________________________________________________________
       (page generated 2025-05-13 23:01 UTC)