2015-08-24 5 views
4

Обратите внимание на C++ 03! любые решения C++ 11 не подходят для меня, но публикуйте их только ради знания.C++ препроцессор условный параметр

Я знаю, что препроцессор может делать такие вещи, как:

#define FOO 4 
#if FOO == 4 
    cout<<"hi"<<endl; 
#endif 

Что мне нужно:

#define BAR(X)\ 
    #if X == 4\ 
     cout<<"hi"<<endl;\ 
    #endif 

main.cpp

BAR(4) 

Я не понимаю, почему все необходимая информация не будет доступна в препроцессорное время.

Итак, скажите, пожалуйста, как достичь такого поведения.


редактировать 1: Нормальный, если условие не будет работать на моем случае, потому что я также делать такие вещи, как:

#define BAR(X)\ 
    #if X == 4\ 
     int poop; 
    #elif 
     double poop; 
    #endif 
+1

Но зачем вам нужно это сделать с препроцессора? – SingerOfTheFall

+2

Нормальное, если условие может работать также ...? – NaCl

+0

Я использую X-macro https://en.wikipedia.org/wiki/X_Macro, и мне нужна условная компиляция при ее расширении – Gulzar

ответ

1

Некоторые ответы здесь были лучше, чем другие. Тот, который я принял, был опубликован Кристианом Кивиетом в комментарии, но он был самым точным для моей цели. Вот расширенная версия:

useCases.h

enum UseCases{ 
    useCase1=0, 
    useCase2, 
    useCaseNumber//always last for iterations 
} 

specializer.h

#include "useCases.h" 
<template UseCases theCase> 
struct StaticCase{ 
    //empty, thus accidents calling from this can't happen 
} 

//specialization 
template<> 
class StaticCase<UseCases::useCase1>{ 
    typedef int T; 
    static foo(T arg){cout<<"case1";}; 
} 


template<> 
class StaticCase<UseCases::useCase2>{ 
    typedef double T; 
    static foo(){cout<<"case2";}; 
} 

Теперь я могу сделать

#define BAR1(useCase) StaticCase<useCase>::foo(); 

или

#define BAR2(useCase) StaticCase<useCase>::T var; 

и вызов:

BAR1(UseCases::useCase1)//output - case1 
BAR1(UseCases::useCase2)//output - case2 
3

Как вы обнаружили, вы не можете сделать это в как вы пытались. Макро-расширение просто не имеет встроенной условной оценки, поэтому вам придется создавать несколько макросов.

Однако, если вы просто пытаетесь «оптимизировать» нормальный поток кода, вы можете положиться на оптимизацию вашего компилятора. Рассмотрим это:

if (true) { 
    std::cout << "Hi\n"; 
} 

В результате программа не будет иметь каких-либо условных проверок в нем, потому что true всегда truthy.

Аналогично:

if (false) { 
    std::cout << "Hi\n"; 
} 

В результате программа не будет содержать код для получения вывода, потому что false никогда не truthy.

Аналогично:

if (4 != 4) { 
    std::cout << "Hi\n"; 
} 

Программа будет по-прежнему не содержит std::cout код.

Во многих случаях, вы можете использовать этот факт, чтобы сохранить код простым и достичь желаемого эффекта:

#define BAR(X) \ 
    if ((X) == 4) { 
     std::cout << "hi" << std::endl;\ 
    } 

Ограничения здесь, конечно, является то, что if заявление должно быть действительным в том месте, напишите BAR(5), или BAR(42) или BAR(999).

Это также гибко, поскольку теперь вы можете использовать значение времени выполнения (например, BAR(i)), и хотя условное выражение больше не может быть свернуто во время компиляции, в таком случае у вас нет причин ожидать, что так или иначе ,

Я использую этот подход в своем макрокоманде регистрации: макрос при вызове для LOG_LEVEL_DEBUG расширяет условное обозначение, которое статически известно никогда, чтобы не совпадать, в версиях сборки.

Идея позволяет компилятору оптимизировать.

Вы также захотите рассмотреть возможность использования a little macro expansion trick to avoid problems with subsequent else clauses.

+0

Спасибо. Это действительно хорошо для оптимизации целей, но здесь это не так. Я добавил ответ, который соответствовал моим потребностям. – Gulzar

1

Если вы можете использовать Boost, вы можете сделать это с помощью Boost.Preprocessor:

#define BAR(X) BOOST_PP_EXPR_IF(BOOST_PP_EQUAL(X, 4), cout << "hi" << endl;) 
+0

Как это там реализовано? – Gulzar

+0

@ Гульзар Вы не хотите знать. Вы действительно не хотите знать ;-) Но если вы все еще это делаете, вы можете искать самого себя - в конце концов, это с открытым исходным кодом. Я боюсь, что нет простого способа обобщить его - это препроцессор черной магии, в значительной степени. – Angew

3

Вы можете сделать это с помощью препроцессора, если область значений для условного параметра хорошо известно (и желательно небольшой). Например, если предположить, что параметр может только имеют значения 0 и 1:

#define DOIT_0(X) 
#define DOIT_1(X) X 
#define CONCAT_(X, Y) X ## Y 
#define MAYBE(X) CONCAT_(DOIT_, X) 

#define BAR(X) MAYBE(X)( cout<<"hi"<<endl; ) 

#define YESNO 0 
BAR(YESNO) 

Live on coliru.

Остерегайтесь незащищенных запятых в аргументе BAR.

Для проверки равенства, опять-таки в небольшом диапазоне:

#define CONCAT3_(X,Y,Z) X ## Y ## Z 
#define EQUAL_0_0(X) X 
#define EQUAL_1_1(X) X 
#define EQUAL_1_1(X) X 
#define EQUAL_0_1(X) 
#define EQUAL_0_2(X) 
#define EQUAL_1_0(X) 
#define EQUAL_1_2(X) 
#define EQUAL_2_0(X) 
#define EQUAL_2_1(X) 
#define DO_IF_EQUAL(X, Y) CONCAT3_(EQUAL_, X, Y) 

#define BAR(X) DO_IF_EQUAL(X, 2) (std::cout << "hi\n";) 
+0

Это очень приятное, но не очень красивое или масштабируемое решение, но +1, потому что оно отвечает на вопрос. Спасибо – Gulzar

+0

@Gulzar FWIW, это почти так, как Boost.Preprocessor реализован внутри. За исключением того, что диапазон значений обычно равен 0..256 и несколько настраивается. – Angew

Смежные вопросы