9

I wish to add some conditional directive in my code to control different build, for example:

#if VERSION > 100
/* Compiling here */
#endif

The problem is that 'VERSION' is in other's code where I can't change. It was defined as a string:

#define VERSION "101"

I'm wondering if there's some sort of macro or directive which converts the string to number so I can simply do

#if STRING_TO_NUMBER(VERSION) > 100
/* Compiling here */
#endif

Is that possible please?

PS. It seems my description is not quite clear. The main purpose of this requirement is to control the version branch. For example, in old version, pre-version 100, this program would like old_function(). After this version, all functions have been migrated to new_function. So I need to write codes like that:

#if VERSION >= 100
    old_function();
#else
    new_function();
#endif
#if VERSION >= 100
int old_function()
{
    ...
}
#else
int new_function()
{
    ...
}
#endif

You can see that only one of the function will be compiled. Therefore the condition must be decided in preprocessing stage, not in the runtime.

The tricky part is, the VERSION had been defined as a string, which brought this question.

Brian Tompsett - 汤莱恩
  • 5,753
  • 72
  • 57
  • 129
user2023470
  • 103
  • 6
  • Does the result need to be available during the preprocessing phase? Or can you use an `inline` function or a `macro`? – Thomas Matthews Nov 20 '13 at 01:30
  • 1
    It's too bad they made the macro a string - it's easy to get a string from a macro's value (using the `#` 'stringize' operator), but it's not easy to go the other way as far as I know. – Michael Burr Nov 20 '13 at 01:57
  • There may be a way to use template meta-programming instead. By essentially invoking or instantiating a different template class based on the version. you would have to create a type based on the VERSION string though. – Rob Nov 20 '13 at 02:09
  • Possible duplicate of http://stackoverflow.com/questions/6982179/opposite-of-c-preprocessor-stringification – Digital Trauma Nov 20 '13 at 02:14
  • I need this to write factorized codes. The compiler will generate different library based executions by different version branch. In that case, it must be done in the preprocessing phase. Otherwise it will hit the link error. – user2023470 Nov 20 '13 at 03:17
  • It depends how much you need the preprocessor. if you need to control `#include` you will have a hard time (see my solution), but if you just need to conditionally call one function or another, you can use rici's solution which is a bit more maintainable – Glenn Teitelbaum Nov 20 '13 at 03:50

2 Answers2

3

If you need to interact with the pre-processor to set other #defines or conditionally #include different headers. Until you can get VERSION to be "fixed" to be an integer...

The only thing that I can think of for you to do is to create a tiny header file defining PROPER_VERSION and include it by naming each file as the version number. So here you would create:

100:
#define PROPER_VERSION 100

101:
#define PROPER_VERSION 101

102:
#define PROPER_VERSION 102

and then you would need to add the following:

#include VERSION

And then use PROPER_VERSION as you need

#if PROPER_VERSION > 100
...

Its not elegant, but I cannot see anything else you can do. You can auto-generate the VERSION file.

Glenn Teitelbaum
  • 10,108
  • 3
  • 36
  • 80
  • Thanks Glenn. It looks no low-cost way to do the conditional compiling. I'll use a script to convert the string. – user2023470 Nov 20 '13 at 04:22
2

As long as you don't need to make declarations or preprocessor defines conditional on VERSION, and as long as you are confident that the VERSION string will just be an integer with no leading zeros, (both of which might be too much to ask for), you might be able to do this at compile time if your compiler has a reasonably effective constant expression evaluator.

For example, gcc 4.8 will optimize away the following if test, leaving only the appropriate arm:

if (strlen(VERSION) > 3 || (strlen(VERSION) == 3 && strcmp(VERSION, "100") > 0)) {
  // code if VERSION is at least "101"
} else {
  // code if VERSION is "100" or less
}

The fact that only one of the branches of the if statement survives the compilation is easily demonstrated by inserting a call to an undefined function in the non-used branch. With gcc (and clang), and with optimization enabled, no linker error is produced:

#include <stdio.h>
#include <string.h>
#define VERSION "101"
// This function is not available for linking
int unknown_function();
// This one is in standard library
int rand();
int main(void) {
    int x;
    if (strlen(VERSION) > 3 || (strlen(VERSION) == 3 && strcmp(VERSION, "100") > 0)) {
      // code if VERSION is at least "101"
      x = rand();
    } else {
      // code if VERSION is "100" or less
      x = unknown_function();
    }
    printf("%d\n", x);
    return 0;
}

(See it at http://ideone.com/nGLGTH)


In C++11, there is an even more clearly compile-time version. You can create a constexpr version of atoi. Combined with what some might call an abuse of templates, that allows for conditional declarations:

constexpr int const_atoi(const char* num, int accum=0) {
  return *num ? const_atoi(num+1, accum*10 + (*num - '0')) : accum;
}

template<bool V_GT_100> struct MoreOrLess_Impl;
template<> struct MoreOrLess_Impl<false> {
  // Old prototype
  void doit(double x) {...}
};
template<> struct MoreOrLess_Impl<true> {
  // New prototype
  void doit(long double x) {...}
};
using MoreOrLess = MoreOrLess_Impl<(const_atoi(VERSION) > 100)>;

// ...
// ... MoreOrLess::doit(x) ...

(Silly example at http://ideone.com/H1sdNg)

rici
  • 234,347
  • 28
  • 237
  • 341
  • This doesn't allow preprocessor directives to say, conditionally include one header or another - otherwise `(VERSION[0]-'0')*100+(VERSION[1]-'0')*10+VERSION[2]-'0'` would suffice – Glenn Teitelbaum Nov 20 '13 at 03:13
  • I'm afraid it won't work. The strlen() is a runtime function. But I need the source code being processed in the compiling stage so I can control the different library branch. – user2023470 Nov 20 '13 at 03:20
  • @user2023470: Most compilers will evaluate `strlen("....")` at compile time; I checked the example I provided and no trace of the `if` is left for runtime. However, it won't let you conditionally #define things, or conditionally #include headers. – rici Nov 20 '13 at 03:22
  • @user2023470: also, the C++11 version is clearly happening at compile time. To the extent that template stuff like that is "clear" :) – rici Nov 20 '13 at 03:23
  • @GlennTeitelbaum: Perhaps I misunderstood your comment. I used the formulation I used (which can be compile-time optimized by gcc) to avoid assuming that VERSION was a particular length. – rici Nov 20 '13 at 03:32
  • @rici yes, that's because the compiler uses the internal strlen instance rather than linking the C library, more like to inline the function. Still it won't help the conditional compiling. Not sure about the C++ version. I'll give it a go. – user2023470 Nov 20 '13 at 03:36
  • @GlennTeitelbaum: You can't use `"101"` in an `#if` directive. You can't use `strcmp` either, so neither of them work with the preprocessor. – rici Nov 20 '13 at 03:43
  • @rici correct - but that was the question posed... How do I use VERSION in a `#if` directive - optimized templates can control code compiled but a lower level – Glenn Teitelbaum Nov 20 '13 at 03:47
  • @Glenn Teitelbaum: I'm afraid that's not preprocessor directives. I'm sure the preprocessor can do some simple arithmetics, but not sure about the string operating. – user2023470 Nov 20 '13 at 03:53
  • @GlennTeitelbaum: True enough. I thought that just answering "No, you can't" wasn't very useful (although whoever answered http://stackoverflow.com/questions/6982179/opposite-of-c-preprocessor-stringification disagreed, and received a lot more support than I will), so I thought I would try to push the boundaries a bit and see what might be possible. The C++11 hack is, at least, interesting. – rici Nov 20 '13 at 03:54
  • Hey guys, according to DigitalTrauma's link, http://stackoverflow.com/questions/6982179/opposite-of-c-preprocessor-stringification, there's no way to destringify in preprocessor. It's quite disappointing. But life is life. Thanks anyway :-) – user2023470 Nov 20 '13 at 03:56
  • @rici - best thing is to get VERSION fixed to be an int. Otherwise he can use your suggestion if it meets constraints or use mine if it doesn't. – Glenn Teitelbaum Nov 20 '13 at 03:57
  • @user2023470 - did you look at my answer? It's not elegant but if you are in a bind... – Glenn Teitelbaum Nov 20 '13 at 03:59
  • @rici, +1 for the C++11 solution is interesting. I'm wondering if it is possible to make the build pass even the non-used `doit` function call some undefined function. Most of the time, the new version code may call some function only in the new header file. – ZijingWu Nov 20 '13 at 08:00