It is possible to call function or calculate in C preprocessor step?

I want to make array whose length is (int)log(macro constant) like

#define MACRO_NUM 100
int arr[(int)log(MACRO_NUM)];

But as you know, MSVC doesn't support variable-length array. So, declaring array, I can't use variable or function at index of array. It means every value determined in run-time couldn't become index of array. So I thought that preprocessor should do some kind of calculation before compilation if to do this possible.

So my question is

  1. Is my inference right?
  2. So is there any ways to make preprocessor calculate? Or preprocessor can do just only replacements.

I know there's another ways like dynamic allocation with pointer etc, but, I just wander ,as a student, It is possible way to do this with array and macro constant when variable-length array is not supported.


If MACRO_NUM is guaranteed to be a compile-time constant (such as 100) and by (int)log(MACRO_NUM) you mean "the largest k such that 2k ≤ MACRO_NUM", then it can be done (but not by getting the preprocessor to do a computation, which is not really possible):

Note: See below for a much more compact version.

#define LOG_MACRO_NUM ((MACRO_NUM >= (1UL<<1)) + \
                       (MACRO_NUM >= (1UL<<2)) + \
                       (MACRO_NUM >= (1UL<<3)) + \
                       (MACRO_NUM >= (1UL<<4)) + \
                       (MACRO_NUM >= (1UL<<5)) + \
                       (MACRO_NUM >= (1UL<<6)) + \
                       (MACRO_NUM >= (1UL<<7)) + \
                       (MACRO_NUM >= (1UL<<8)) + \
                       (MACRO_NUM >= (1UL<<9)) + \
                       (MACRO_NUM >= (1UL<<10)) + \
                       (MACRO_NUM >= (1UL<<11)) + \
                       (MACRO_NUM >= (1UL<<12)) + \
                       (MACRO_NUM >= (1UL<<13)) + \
                       (MACRO_NUM >= (1UL<<14)) + \
                       (MACRO_NUM >= (1UL<<15)) + \
                       (MACRO_NUM >= (1UL<<16)) + \
                       (MACRO_NUM >= (1UL<<17)) + \
                       (MACRO_NUM >= (1UL<<18)) + \
                       (MACRO_NUM >= (1UL<<19)) + \
                       (MACRO_NUM >= (1UL<<20)) + \
                       (MACRO_NUM >= (1UL<<21)) + \
                       (MACRO_NUM >= (1UL<<22)) + \
                       (MACRO_NUM >= (1UL<<23)) + \
                       (MACRO_NUM >= (1UL<<24)) + \
                       (MACRO_NUM >= (1UL<<25)) + \
                       (MACRO_NUM >= (1UL<<26)) + \
                       (MACRO_NUM >= (1UL<<27)) + \
                       (MACRO_NUM >= (1UL<<28)) + \
                       (MACRO_NUM >= (1UL<<29)) + \
                       (MACRO_NUM >= (1UL<<30)) + \
                       (MACRO_NUM >= (1UL<<31)))
/* If you want to handle 64-bit numbers, change 1UL to 1ULL
 * and continue the pattern up to 63.
 */

Live on coliru

This can be made much more compact using the Boost preprocessor library, which unlike most of Boost works with both C and C++. It's a separate header-only library, so it can be installed independently by just copying the directory of header files. That reduces the above ugliness to:

#include <stdio.h>
#include <boost/preprocessor/repetition/repeat_from_to.hpp>
#define CHECK_POWER(Z,I,V) + ((V) >= (1ULL << I))
#define ILOGB(C) (BOOST_PP_REPEAT_FROM_TO(1, 32, CHECK_POWER, C))

/* Declared at file scope to show that the macro produces a
 * compile-time constant; static VLAs are not allowed
 * even on compilers which implement VLAs.
 */
int arr[ILOGB(4127)] = {0};
int main(void) {
    printf("array has %zu elements.\n", sizeof arr / sizeof *arr);
}

Live on coliru