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