Существуют ли в препроцессоре C/C++ директивы для преобразования строки в число?

Я хочу добавить в свой код условную директиву для управления разными сборками, например:

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

Проблема в том, что «ВЕРСИЯ» находится в другом коде, который я не могу изменить. Он был определен как строка:

#define VERSION "101"

Мне интересно, есть ли какой-то макрос или директива, которая преобразует строку в число, поэтому я могу просто сделать

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

Пожалуйста, это возможно?

PS. Кажется, мое описание не совсем понятно. Основная цель этого требования — контролировать ветку версии. Например, в старой версии, до версии 100, эта программа хотела бы использовать old_function(). После этой версии все функции были перенесены в new_function. Поэтому мне нужно написать такие коды:

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

Вы можете видеть, что будет скомпилирована только одна из функций. Следовательно, условие должно быть определено на этапе предварительной обработки, а не во время выполнения.

Сложность в том, что ВЕРСИЯ была определена как строка, которая вызвала этот вопрос.


person user2023470    schedule 20.11.2013    source источник
comment
Должен ли результат быть доступен на этапе предварительной обработки? Или вы можете использовать функцию inline или macro?   -  person Thomas Matthews    schedule 20.11.2013
comment
Жаль, что они сделали макрос строкой — получить строку из значения макроса легко (используя оператор # 'stringize'), но, насколько я знаю, пойти другим путем непросто.   -  person Michael Burr    schedule 20.11.2013
comment
Вместо этого может быть способ использовать метапрограммирование шаблонов. По сути, вызывая или создавая экземпляр другого класса шаблона на основе версии. вам нужно будет создать тип на основе строки VERSION.   -  person Rob    schedule 20.11.2013
comment
Мне это нужно для написания факторизованных кодов. Компилятор будет генерировать различные исполнения на основе библиотек для разных ветвей версии. В этом случае это должно быть сделано на этапе предварительной обработки. В противном случае это приведет к ошибке ссылки.   -  person user2023470    schedule 20.11.2013
comment
Это зависит от того, насколько вам нужен препроцессор. если вам нужно контролировать #include, вам будет трудно (см. мое решение), но если вам просто нужно условно вызвать ту или иную функцию, вы можете использовать решение rici, которое немного более удобно в обслуживании   -  person Glenn Teitelbaum    schedule 20.11.2013


Ответы (2)


Если вам нужно взаимодействовать с препроцессором, чтобы установить другие #defines или условно #include разные заголовки. Пока вы не сможете заставить VERSION быть "фиксированным" целым числом...

Единственное, что я могу придумать для вас, это создать крошечный заголовочный файл, определяющий PROPER_VERSION, и включить его, назвав каждый файл номером версии. Итак, здесь вы должны создать:

100:
#define PROPER_VERSION 100

101:
#define PROPER_VERSION 101

102:
#define PROPER_VERSION 102

а затем вам нужно будет добавить следующее:

#include VERSION

А потом используйте PROPER_VERSION как вам нужно

#if PROPER_VERSION > 100
...

Это не элегантно, но я не вижу ничего другого, что вы могли бы сделать. Вы можете автоматически сгенерировать файл VERSION.

person Glenn Teitelbaum    schedule 20.11.2013
comment
Спасибо, Гленн. Это не выглядит дешевым способом условной компиляции. Я буду использовать скрипт для преобразования строки. - person user2023470; 20.11.2013

Пока вам не нужно делать объявления или определения препроцессора условными для VERSION, и пока вы уверены, что строка VERSION будет просто целым числом без начальных нулей (и то, и другое может быть слишком много, чтобы просить ), вы можете сделать это во время компиляции, если ваш компилятор имеет достаточно эффективный оценщик константных выражений.

Например, gcc 4.8 оптимизирует следующий тест if, оставив только соответствующую руку:

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
}

Тот факт, что только одна из ветвей оператора if переживает компиляцию, легко продемонстрировать, вставив вызов неопределенной функции в неиспользуемую ветвь. С gcc (и clang) и с включенной оптимизацией ошибка компоновщика не возникает:

#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;
}

(См. на http://ideone.com/nGLGTH)


В C++11 есть еще более четкая версия времени компиляции. Вы можете создать constexpr версию atoi. В сочетании с тем, что некоторые могут назвать злоупотреблением шаблонами, это позволяет использовать условные объявления:

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) ...

(Глупый пример на http://ideone.com/H1sdNg)

person rici    schedule 20.11.2013
comment
Это не позволяет директивам препроцессора сказать, условно включать тот или иной заголовок - в противном случае было бы достаточно (VERSION[0]-'0')*100+(VERSION[1]-'0')*10+VERSION[2]-'0' - person Glenn Teitelbaum; 20.11.2013
comment
Боюсь, это не сработает. strlen() — это функция времени выполнения. Но мне нужно, чтобы исходный код обрабатывался на этапе компиляции, чтобы я мог управлять другой ветвью библиотеки. - person user2023470; 20.11.2013
comment
@ user2023470: Большинство компиляторов оценивают strlen("....") во время компиляции; Я проверил приведенный мной пример, и во время выполнения не осталось следов if. Однако это не позволит вам условно #определить вещи или условно #включить заголовки. - person rici; 20.11.2013
comment
@user2023470 user2023470: кроме того, версия C++11 явно происходит во время компиляции. Насколько понятны такие шаблонные вещи :) - person rici; 20.11.2013
comment
@GlennTeitelbaum: Возможно, я неправильно понял ваш комментарий. Я использовал формулировку, которую я использовал (которая может быть оптимизирована во время компиляции с помощью gcc), чтобы не предполагать, что ВЕРСИЯ имеет определенную длину. - person rici; 20.11.2013
comment
@rici да, это потому, что компилятор использует внутренний экземпляр strlen, а не связывает библиотеку C, больше похоже на встроенную функцию. Тем не менее, это не поможет условной компиляции. Не уверен насчет версии C++. Я попробую. - person user2023470; 20.11.2013
comment
@GlennTeitelbaum: вы не можете использовать "101" в директиве #if. Вы также не можете использовать strcmp, поэтому ни один из них не работает с препроцессором. - person rici; 20.11.2013
comment
@rici правильно - но это был поставленный вопрос ... Как использовать VERSION в директиве #if - оптимизированные шаблоны могут управлять скомпилированным кодом, но более низкого уровня - person Glenn Teitelbaum; 20.11.2013
comment
@Glenn Teitelbaum: боюсь, это не директивы препроцессора. Я уверен, что препроцессор может выполнять некоторые простые арифметические действия, но не уверен в работе со строками. - person user2023470; 20.11.2013
comment
@GlennTeitelbaum: Совершенно верно. Я думал, что просто ответ «Нет, вы не можете» не очень полезен (хотя тот, кто ответил stackoverflow.com/questions/6982179/ не согласился и получил гораздо больше поддержки, чем я), поэтому я подумал, что попытаюсь немного раздвинуть границы и посмотреть, что может быть возможно . Хак C++11, по крайней мере, интересен. - person rici; 20.11.2013
comment
Привет, ребята, согласно ссылке DigitalTrauma, stackoverflow.com/questions /6982179/, в препроцессоре невозможно удалить строки. Это очень разочаровывает. Но жизнь есть жизнь. Спасибо, в любом случае :-) - person user2023470; 20.11.2013
comment
@rici - лучше всего исправить VERSION как int. В противном случае он может использовать ваше предложение, если оно соответствует ограничениям, или использовать мое, если оно не соответствует. - person Glenn Teitelbaum; 20.11.2013
comment
@user2023470 user2023470 - ты смотрел мой ответ? Это не элегантно, но если вы в безвыходном положении... - person Glenn Teitelbaum; 20.11.2013
comment
@rici, +1 за решение С++ 11 интересно. Мне интересно, можно ли сделать так, чтобы сборка проходила, даже если неиспользуемая функция doit вызывает какую-то неопределенную функцию. В большинстве случаев код новой версии может вызывать некоторые функции только в новом заголовочном файле. - person ZijingWu; 20.11.2013