2010-02-03 3 views
20
my_macro << 1 << "hello world" << blah->getValue() << std::endl; 

должен расширяться в:Можно ли написать следующее как макрос C++?

std::ostringstream oss; 
oss << 1 << "hello world" << blah->getValue() << std::endl; 
ThreadSafeLogging(oss.str()); 
+0

Интересно, можете ли вы сделать #define my_macro (blah) {std :: ostringstream oss; oss blah; ThreadSafeLogging (oss.str()); } –

+0

См. Также: https://stackoverflow.com/questions/4446484/a-line-based-thread-safe-stdcerr-for-c –

ответ

72
#define my_macro my_stream() 
class my_stream: public std::ostringstream { 
public: 
    my_stream() {} 
    ~my_stream() { 
     ThreadSafeLogging(this->str()); 
    } 
}; 
int main() { 
    my_macro << 1 << "hello world" << std::endl; 
} 

временный тип my_stream создан, который является подклассом ostringstream. Все операции с этой временной работой, как на ostringstream.

Когда инструкция заканчивается (т. Е. Сразу после точки с запятой на всей операции печати в main()), временный объект выходит за пределы области действия и уничтожается. Деструктор my_stream вызывает ThreadSafeLogging с ранее собранными данными.

Протестировано (g ++).

Спасибо/кредиты dingo за указание на то, как упростить все это, поэтому мне не нужен перегруженный operator<<. Слишком плохие авансы не могут быть разделены.

+7

Это самое яркое использование (или злоупотребление) деструкторов C++, которые я видел в своей жизни. – anon

+0

Нет, макрос создает временное, которое уничтожается после того, как строка, содержащая временные функции, выполняется. –

+0

Я забираю проклятие деструктора; это немедленно разрушает из-за создаваемого темпа. Красиво сделано. – MikeSep

2

Нет. Проблема заключается в том, что без использования синтаксиса функции, макрос ограничивается только заменяются, где это.

Но если вы были готовы использовать синтаксис функций, вы можете заменить материал как до, так и после args.

my_macro(1 << "hello world" << blah->getValue() << std::endl); 

Вы могли бы, определив MyMacro как:

#define my_macro(args) std::ostreamstring oss; \ 
         oss << args; \ 
         ThreadSafeLogging(oss.str()); 
+5

Учитывая ответ Николоса, кажется, что «Нет», это неверно. –

3

Не могли бы вы просто проистекать из ostream и обеспечить собственную реализацию в потоковом режиме? Тогда вы могли бы просто сделать

myCOutObject << 1 << "hello world" << blah->getValue() << std::endl; 

И получить ту же функциональность без макросов и с помощью C++ правильно?

2

Взгляните на google-glog, они делают это с помощью временного объекта инстанциирован с

LOG(INFO) << "log whatever" << 1; 

и они также имеют другие интересные макросы, такие как LOG_IF и др.

1

Вот еще один неприятный трюк, который я видел где-то в другом месте. У этого есть существенный недостаток по сравнению с моим другим ответом: вы не можете использовать его дважды в той же области, потому что он объявляет переменную. Тем не менее, это может быть интересно для других случаях, где вы хотите иметь somemacro foo запустите что-то послеfoo.

#define my_macro \ 
    std::ostringstream oss; \ 
    for (int x=0; x<2; ++x) \ 
     if (x==1) ThreadSafeLogging(oss.str()); \ 
     else oss 

int main() { 
    my_macro << 1 << "hello world" << std::endl; 
} 
2

Конечно, вы можете использовать его более одного раза :)! __LINE__ макрос определяется всеми стандартными компиляторами. Таким образом, мы можем использовать его для создания ostrinstream имя переменной :)

#define Var_(Name, Index) Name##Index 
#define Var(Name, Index) Var_(Name, Index) 
#define my_macro \ 
    std::ostringstream Var(oss, __LINE__);   \ 
for (int x=0; x<2; ++x) \ 
    if (x==1) std::cout << Var(oss, __LINE__).str(); \ 
    else Var(oss, __LINE__) 

Хорошо хорошо не два раза на одной и той же линии, р, но .. кто бы сделать это? !!

int main() { 
    my_macro << 4 << " hello " << std::endl; 
    my_macro << 2 << " world !" << std::endl; 
} 
+0

Зачем ему это имя? Вы можете использовать объект MyObject() 'как временный и полагаться на его разрушение в конце инструкции. – dascandy

+0

Умный! Обратите внимание, что во всех популярных компиляторах вы можете использовать нестандартный '__COUNTER__' вместо стандартного' __LINE__', а затем он будет работать даже несколько раз в одной строке. Однако в любом случае этот макрос не является гигиеничным; если вы скажете 'if (log) my_macro << 4 << std :: endl;', тогда у вас будет плохое время. Ответ на верхний голос, основанный на трюке деструктора google-glog's LOG(), является гигиеничным. – Quuxplusone

2

Настройка протоколирования У меня есть очень похожа:

bool ShouldLog(const char* file, size_t line, Priority prio); 

class LoggerOutput : public std::stringstream { 
public: 
    LoggerOutput(const char* file, size_t line, Priority prio) 
    : prio(prio) 
    { 
    Prefix(file, line, prio); 
    } 
    void Prefix(const char* file, size_t line, Priority prio); 
    ~LoggerOutput() { 
    Flush(); 
    } 
    void Flush(); 
private: 
    Priority prio; 
}; 

#define LOG(Prio) if (!Logging::ShouldLog(__FILE__, __LINE__, Prio)) {} else Logging::LoggerOutput(__FILE__, __LINE__, Prio) 

Если ведение журнала отключено, то ostream никогда не создается и мало накладных расходов существует. Вы можете настроить ведение журнала на имя файла & номер (номера) линии или уровни приоритета. Функция ShouldLog может меняться между вызовами, поэтому вы можете дросселировать или ограничивать вывод. Выход журнала использует две функции для само модификации: префикс, который добавляет префикс «file: line: (PRIO)» в строку, и Flush(), который одновременно сбрасывает его на выход журнала как одну команду и добавляет к нему новую строку , В моей реализации это всегда делает, но вы можете сделать это условным, если его еще нет.

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