Есть ли способ написать следующее как макрос C ++? - PullRequest
19 голосов
/ 04 февраля 2010
my_macro << 1 << "hello world" << blah->getValue() << std::endl;

должно расшириться до:

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

Ответы [ 7 ]

72 голосов
/ 04 февраля 2010
#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 ++).

Благодарность / кредит Динго за указание наупростить все это, так что мне не нужен перегруженный operator<<.Жаль, что голос не может быть передан.

3 голосов
/ 04 февраля 2010

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

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

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

2 голосов
/ 16 марта 2010

Учитывая, что эти строки включены в ваш код, да, возможно, макрос

#include <iostream>
#include <sstream> 

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

Вот новая версия, которая рассматривается только как инструкция с одним оператором: (EDITED)

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

// So you can use it like this 
int main() 
{ 
    if (4 != 2)
        my_macro << 4 << " hello "  << std::endl; 
    my_macro << 2 << " world !" << std::endl; 
} 

Вероятно, Developper не нужно будет использовать этот макрос дважды в одной строке из-за простоты оператора <<.Но если вам это нужно, вы можете переключить использование __LINE__ на __COUNTER__ (что не является стандартным!).Спасибо Quuxplusone за этот совет

2 голосов
/ 04 февраля 2010

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

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

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

2 голосов
/ 04 февраля 2010

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

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

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

Вы можете определить MyMacro как:

#define my_macro(args) std::ostreamstring oss; \
                       oss << args; \
                       ThreadSafeLogging(oss.str());
1 голос
/ 06 июля 2017

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

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 (), который сбрасывает его в вывод журнала в виде одной команды и добавляет к нему новую строку,В моей реализации это всегда так, но вы можете сделать это условным, если его там еще нет.

1 голос
/ 06 февраля 2010

Вот еще один неприятный трюк, который я видел где-то еще. Он имеет существенный недостаток по сравнению с моим другим ответом: вы не можете использовать его дважды в одной и той же области видимости, потому что он объявляет переменную. Тем не менее, это все еще может быть интересно для других случаев, когда вы хотите, чтобы 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;
}
...