2015-02-18 2 views
4

Хорошо известно, что вы можете использовать макросы для создания версии printf, которая может быть удалена из кода во время компиляции (скажем, если вы хотите печатать только на отладочных сборках). Полученный код можно использовать точно так же, как и использовать printf.Можно ли «скомпилировать» потоковые выражения в C++?

Возможно ли создать аналогичный сценарий по отношению к потоку?

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

#include <iostream> 

class Foo 
{ 
public: 
    template <typename T> 
    Foo& operator <<(const T& input) 
    { 
     std::cout << input; 
    } 
}; 

int ComputeExpensiveThing(); // this function is expensive 

void doSomething() 
{ 
    Foo() << "Expensive thing: " << ComputeExpensiveThing() << std::endl; 
    // do other things 
} 

Есть ли способ условно раздеть первую линию DoSomething() во время компиляции?

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

#define FOO if(!someGlobalCondition); else Foo() 

void doSomething() 
{ 
    FOO << "Expensive thing: " << ComputeExpensiveThing() << std::endl; 
} 

Однако это требует команда условного перехода каждый раз, когда мы используем FOO. Это значительное улучшение по сравнению с вызовом ComputeExpensiveThing, но я бы предпочел удалить лишние ветки.

Я также видел альтернативу, которая изменяет целевой синтаксис, что-то вроде

void doSomething() 
{ 
    Foo("Expensive thing: " << ComputeExpensiveThing() << std::endl); 
} 

но полученный синтаксис является немного странным и неинтуитивным для некоторых пользователей.

+0

Возможно, было бы лучше разделить это на отдельные вопросы. – djs

+0

@ djs: Здесь я вижу только один вопрос. Что еще (что нужно отделить)? – Cornstalks

+0

Второй смелый вопрос должен быть конкретным примером, разъясняющим смысл первого. Извините, если это сбивает с толку! – bobobo

ответ

3

Почему бы вам просто не сделать условное что-то вроде этого?

#ifdef DEBUG 
#define dbglog std::cout 
#else 
#define dbglog if(0) std::cout 
#endif 

Затем вы можете использовать его так:

dbglog << "Hello, World!" << std::endl; 

if(0) и последующее заявление будет полностью удалено компилятором (лязг даже делает эту оптимизацию с не -O флага указанного!).

+0

Компилятор может оптимизировать 'ComputeExpensiveThing()', если он уверен, что функция не имеет побочных эффектов. Со сложным вычислением это может быть не так. – sth

+0

Что-то, что работало с существующими базами кода и не требовало модификации каждого сайта вызова, было бы * хорошим * (но, возможно, не возможным). –

+0

Ну, если вы уже используете, например, 'cout', тогда вы могли бы в принципе заменить его макросом. Однако термин «существующие кодовые базы» довольно широк; очень вероятно, что многие существующие базы кода уже будут использовать пользовательскую регистрацию, которую можно легко модифицировать, используя мой ответ. – nneonneo

1

Я не очень хороший программист на С ++, и я мог легко ошибаться.

Но не должно быть точно таким же, если вы определили const debug и просто сделаете if (debug) do_something;. Я собираю, что компиляторы любят constant folding, и он будет оценивать все if s до времени, и не было бы никаких ветвей, созданных вообще.

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

Однако это что-то полностью связано с компилятором, поэтому я не могу претендовать на универсальность.

+1

Это хороший момент. Пока тестовое условие является постоянным выражением, если компилятор вообще хорош в своей работе, он должен просто полностью исключить утверждение 'if'. Это может закончиться ситуацией «выкапывать в сборку», а не «использовать злые/умные (clevil?) Макро-трюки». – bobobo

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