2

Not this: How to generate a compilation error when pointer types differ?

That is useful but I wanted to block values, not types. For example:

#include <stdio.h>
#include <conio.h>

char * num_sys_convert(int num_value,  int old_base, int targeted_base) {
    /* These are the codes. Only clever people can see it. The king would surely be pleased to see this. */
}

int main() {
    printf("%s",num_sys_convert(10011,2,10));    // this should be fine
    printf("%s",num_sys_convert(10021,2,10));    // this should generate a compile-time error
    getch();
}

I'm trying to make a header, in case of there will be mistake(s) while typing, I wanted to add a little function to help that out. Since the old base is binary, the digit 2 (or higher) should NOT exist in the decimal representation of the given number.

Is it possible in C to block certain values to go into the function as parameter at compilation time?

dbush
  • 205,898
  • 23
  • 218
  • 273

1 Answers1

4

This is an ugly cludge that will be specific to this function, but you can do it like this:

#define num_sys_convert(x, b1, b2) \
    ((void)(struct digits { \
        int d1:(( (x)             %10 >= (b1) ? -1 : 1)); \
        int d2:((((x) /        10)%10 >= (b1) ? -1 : 1)); \
        int d3:((((x) /       100)%10 >= (b1) ? -1 : 1)); \
        int d4:((((x) /      1000)%10 >= (b1) ? -1 : 1)); \
        int d5:((((x) /     10000)%10 >= (b1) ? -1 : 1)); \
        int d6:((((x) /    100000)%10 >= (b1) ? -1 : 1)); \
        int d7:((((x) /   1000000)%10 >= (b1) ? -1 : 1)); \
        int d8:((((x) /  10000000)%10 >= (b1) ? -1 : 1)); \
        int d9:((((x) / 100000000)%10 >= (b1) ? -1 : 1)); \
        int da:((((x) /1000000000)%10 >= (b1) ? -1 : 1)); \
    }){0}, num_sys_convert_real(x, b1, b2))


char * num_sys_convert_real(int num_value,  int old_base, int targeted_base) {
    ...
}

The real function is wrapped by a call to the above macro. This macro defines a struct with a number of bitfields, where the length of each bitfield is dependent on a specific decimal digit of the first argument. If that digit is greater than or equal to the value of the second argument (i.e. the base), the size of the bitfield is set to -1, which is invalid and produces a compliation error.

If the number does look like a valid number for the given base, the end result is that an temporary object of the given struct type is created via a compound literal and used in an expression as the left side of the comma operator, meaning its value is discarded, and the actual function call is the right side of the comma operator.

This works under the assumption that:

  • The first argument to the function is decimal integer literal
  • The second argument to the function is an integer literal
  • The literal has type int, i.e. no type suffix
  • An int is 32 bits in length

It will always fail to compile if you do this:

int value = 101;
num_sys_convert(value, 2, 10);

Or this:

int base = 2;
num_sys_convert(101, base, 10);

Because an expression used to set the size of a bitfield must be a compile time constant expressions.

Also, this would fail the check:

num_sys_convert(0x10, 2, 10);

Because the hex value 0x10 has the decimal value 16.

And this would pass the check:

num_sys_convert(0xa, 2, 10);

Because the hex value 0xa has the decimal value 10.


As this is such a cludge, don't do this!

Rather than attempting to check parameters at compile time, the proper thing to do is to validate the parameters at run time inside of the function and to return some sort of error code if the parameters are invalid.

dbush
  • 205,898
  • 23
  • 218
  • 273
  • And... of course you can skip this test with `(num_sys_convert)(911, base, 10)` and by explicitly declaring the function without the macro and... – Antti Haapala -- Слава Україні Dec 26 '18 at 14:23
  • LOL, that's funny but better than nothing. (I think I'll use this if there are no more better answers/suggestions, XD.) –  Dec 26 '18 at 15:09
  • @LaiYanHui While this "works", the best thing to do is perform the check at run time. Trying to do something like this is asking for trouble. – dbush Dec 26 '18 at 15:11
  • Roger that. Actually I had thought of doing that during run time but it would result in building a program instead of making a header so I gave this a try. #wink –  Dec 26 '18 at 15:14
  • This is a funny answer. +1 for not being useful ^^ – YSC Dec 26 '18 at 15:47