Нужно ли писать следующее как макрос С++?

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

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

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

Ответ 1

#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<<. Слишком плохие upvotes не могут быть разделены.

Ответ 2

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

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

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

Ответ 3

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

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

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

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

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

Ответ 4

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

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

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

Ответ 5

Конечно, вы можете использовать его более одного раза:)! Макрос __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; 
} 

Ответ 6

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

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

Ответ 7

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