Есть ли способ написать следующее как макрос C++?

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

следует расширить в:

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

7 ответов

Решение
#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<<, Слишком плохими голосами нельзя поделиться.

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

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

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

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

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

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

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

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

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

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

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

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

#include <iostream>
#include <sstream> 

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

Вот новая версия, которая рассматривается только как инструкция с одним утверждением: (ИЗМЕНЕНО)

#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 за этот совет

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

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

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

Другие вопросы по тегам