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.