4 ms·
Does anybody have a real case study about the benefits of using constexpr? Everyone is raving about it but their examples of where to use it are absolutely triv
by ulnarkressty 5y ago
Does anybody have a real case study about the benefits of using constexpr? Everyone is raving about it but their examples of where to use it are absolutely trivial (e.g. return 1+2) and I can't really think of any real-world programs that would attempt to do such computations in a non-dynamic way.
- comex 5y agoJust as a starting point, you can look at any existing uses of #define to either define a constant or define a ‘function’ that can be evaluated at compile time. For constants, #define is ubiquitous and works fairly well, but it has several disadvantages including: - When a macro quotes some code, e.g. when assert() shows the expression that failed, any uses of macros inside that code will show up fully expanded. This makes it harder to tell what’s going on. - If the definition of the constant has any complexity to it, it has to be re-evaluated every time it’s used. - Doesn’t work with debuggers in some cases (admittedly this could be fixed without a new language feature). - Difficult to work with for tools that generate FFI bindings for other languages. They can’t easily tell what’s a constant that should have bindings generated for it, versus what’s some random piece of syntax. An alternative way to define constants in C is with anonymous enum declarations; they avoid all of the above issues, but they can only create constants of integer type – not structs or floating point values – and on some compilers are even more restricted to only creating constants of type `int` or `unsigned int`. For functions: There are many function-like macros in C APIs. Some must be macros because they're type-generic or otherwise go beyond what a regular function can do. Others could work just as well as inline functions, even in current C, and are macros only because they're inherited from days when compilers couldn't reliably inline functions. But others are macros because they're meant to be evaluated inside compile-time constants. For example, from OpenBSD's implementation of the Unix socket API: #define CMSG_SPACE(len) (_ALIGN(sizeof(struct cmsghdr)) + _ALIGN(len)) #define _ALIGN(p) (((unsigned long)(p) + _ALIGNBYTES) & ~_ALIGNBYTES) CMSG_SPACE calculates the required buffer size for a control message structure of a given body size. One of the examples from the relevant man page [1] has: union { struct cmsghdr hdr; unsigned char buf[CMSG_SPACE(sizeof(int))]; } cmsgbuf; which requires CMSG_SPACE(sizeof(int)) to be a compile-time constant. Mind you, this is all just a starting point. There are more ambitious things you can do with constexpr that people aren't doing with #define because it's not powerful enough. For example: if you use printf, modern compilers have built-in functionality to validate the format string against the argument types. But what if you want to define your own variadic function (for formatting or any other purpose) with a different format string mini-language? With constexpr you can do your own validation at compile time. (It's a bit easier in C++, but it should be doable in C as well if constexpr is added, by using a wrapper macro.) [1] https://man.openbsd.org/CMSG_DATA.3 https://man.openbsd.org/CMSG_DATA.3