Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Are there directives in C/C++ preprocessor to convert string to number?

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.

like image 692
user2023470 Avatar asked Nov 20 '13 01:11

user2023470


2 Answers

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.

like image 104
Glenn Teitelbaum Avatar answered Oct 09 '22 00:10

Glenn Teitelbaum


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)

like image 2
rici Avatar answered Oct 09 '22 01:10

rici