8

Parts of my code depend on the value of a preprocessor symbol:

int a()
{
#if SDK_VERSION >= 3
    return 1;
#else
    return 2;
#endif
}

The comparison depends of the value of SDK_VERSION. It is expected to be a integer or something that compares to a integer, in this case, 3. If SDK_VERSION is something that cannot be compared to a integer, there will be a compile error.

Is there a way to abort the compilation if SDK_VERSION is not of an expected type? For example:

#if type(SDK_VERSION) != int  # Does not compile, I know
#error "SDK_VERSION must be an integer."
#endif
Brian Tompsett - 汤莱恩
  • 5,753
  • 72
  • 57
  • 129
GDICommander
  • 1,273
  • 3
  • 15
  • 27
  • 1
    The error message would be a little more obscure, but putting something like `int ___SDK_VERSION_SHOULD_BE_CONVERTIBLE_TO_INT___ = SDK_VERSION;` in the header that includes `SDK` might solve your problem. – Frédéric Hamidi Jan 23 '12 at 19:11
  • 3
    You say you already get a compilation error if `SDK_VERSION` is of the wrong type, is what you want a nicer/different error message? – drrlvn Jan 23 '12 at 19:12
  • @FrédéricHamidi: Ignoring the problems associated with double underscore in your identifiers. – Martin York Jan 23 '12 at 19:13
  • @Loki, they are triple underscores, actually, just in case :) – Frédéric Hamidi Jan 23 '12 at 19:14
  • Note: If you do not define an actual value for a macro (and it is set on the coomand line) it defaults to 1. e.g. `g++ -DPLOP code.cpp` Here PLOP is 1. – Martin York Jan 23 '12 at 19:17
  • 1
    @FrédéricHamidi: It does not matter. 2 underscores in a row is reserved by the implementation. See http://stackoverflow.com/a/228797/14065 (as are identifiers begining with an underscore and an uppercase letter). – Martin York Jan 23 '12 at 19:19
  • @Loki, I didn't know that, thanks for the pointer :) I guess this means we would have to use a reasonably unique prefix instead, which is even more kludgey... – Frédéric Hamidi Jan 23 '12 at 19:25

7 Answers7

14

Use template to generate such error:

template<typename T> struct macro_error;

template<> struct macro_error<int> {};

template<typename T> void check(T) 
{ 
   macro_error<T> SDK_VERSION_must_be_int; 
}
int ignored = (check(SDK_VERSION), 0);

This code would generate compilation error, having the following string in it, if the SDK_VERSION is not int:

SDK_VERSION_must_be_int

See these demo:

And also notice the error message in the first case. It prints this:

prog.cpp:9: error: ‘SDK_VERSION_must_be_int’ has incomplete type
prog.cpp:9: warning: unused variable ‘SDK_VERSION_must_be_int’
Nawaz
  • 353,942
  • 115
  • 666
  • 851
5

Nope. The reason is that the preprocessor symbol doesn't have a type at all.

Preprocessor symbols are best thought of as being just barely more than a string. C's type system doesn't really exist until the compilation step, which happens after the preprocessor has had its go.

Matt
  • 10,434
  • 1
  • 36
  • 45
  • Ooh, though I like Jason's answer - clever, but be sure to add some documentation, because when compilation fails, it's not too clear that the intent of the snippet was to check the type of SDK_VERSION. – Matt Jan 23 '12 at 19:14
2

This won't compile if SDK_VERSION is a string (but it would work for a float ...):

int get_SDK_Version()
{
    return SDK_VERSION;
}
Jason Sundram
  • 12,225
  • 19
  • 71
  • 86
1

The preprocessor has no knowledge of types or any of the language keywords.

ouah
  • 142,963
  • 15
  • 272
  • 331
0

In fact, the #if directive considers what is to its right as integers.

So if you want to achieve what you asked for, you have to do some arithmetics. For example do a test.c file with:

#define VERSION 7

#if VERSION
  #if VERSION - (VERSION % 10 )
    #warning Number out of range (1-9)
  #else
    #warning Number in range (1-9)
  #endif
#else
  #warning Zero or not a number
#endif

Compile with

gcc -c -o /dev/null test.c

You will the get the message: "Zero of not a number"... if your VERSION is 0, or does not evaluates (preprocessor-wise) as an integer.

And if VERSION evalutates as an integer, you will get the first or second message according to its value.

This will allow you to do what you were searching for.

Documentation on #if : http://gcc.gnu.org/onlinedocs/cpp/If.html

Note that integer can be expressed like: 123 or 0xCC or anything that evaluates to an integer constant after recursive macro expansion.

If the number is a floating point like: 3.14 it is considered as zero.

You cannot simply distinguish 0 (the integer) with something that is not an integer. There is probably a possibility though, using macro string concatenation, but it remains to be explored.

user941239
  • 865
  • 8
  • 5
0

Excellent idea @Navaz ! I had to find for particular type of parameter made simple adjustment and easily found remaining calls of not listed types in case:

template<typename T> struct ints_error;
template<typename T> struct double_error;

template<> struct ints_error<uint8_t> {};
template<> struct ints_error<uint16_t> {};
template<> struct ints_error<int64_t> {};
template<> struct double_error<double> {};

template<typename T> int check_int(T)
{
    ints_error<T> must_be_listed;
    (void)must_be_listed;
    return 0;
}
template<typename T> int check_double(T)
{
    double_error<T> must_be_double;
    return must_be_double, 0;
}
#define sqlite3_bind_int(a,b,c) check_int(c);
#define sqlite3_bind_int64(a,b,c) check_int(c); 
#define sqlite3_bind_double(a,b,c) check_double(c);
Jan
  • 2,178
  • 3
  • 14
  • 26
0

I'm thinking you could pull some template meta-programming off here.

I'll try some magic of my own and post an example if I get something working. My theory is that you could instantiate a class or function template. The template's default definition would result in a compilation error if it is not of the appropriate type. A template specialization for int would not result in an error however.

This is out of the range of my experience with template and preprocessor magic, but worth a look into. An issue I expect is that SDK_VERSION isn't really anything more than a symbol to be substituted. SDK_VERSION doesn't have a type, per-se.

At best, I think you can check what types it can convert to, but the concept of a preprocessor defined "constant" is very... brute force, almost hackish. It may be a common practice, but it's not at all safe. It's best to remember they're just a preprocessor version of find-and-replace.

Sion Sheevok
  • 4,057
  • 2
  • 21
  • 37