my_macro << 1 << "hello world" << blah->getValue() << std::endl;
следует расширять на:
std::ostringstream oss;
oss << 1 << "hello world" << blah->getValue() << std::endl;
ThreadSafeLogging(oss.str());
my_macro << 1 << "hello world" << blah->getValue() << std::endl;
следует расширять на:
std::ostringstream oss;
oss << 1 << "hello world" << blah->getValue() << std::endl;
ThreadSafeLogging(oss.str());
#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 не могут быть разделены.
Не могли бы вы просто извлечь из ostream и обеспечить свою собственную поточную безопасную реализацию? Тогда вы могли бы просто сделать
myCOutObject << 1 << "hello world" << blah->getValue() << std::endl;
И получить ту же самую функциональность без макросов и правильно использовать С++?
Нет. Проблема в том, что без использования функционального синтаксиса макрос ограничивается заменой только там, где он есть.
Но если вы хотите использовать функциональный синтаксис, вы можете заменить материал как до, так и после аргументов.
my_macro(1 << "hello world" << blah->getValue() << std::endl);
Вы можете определить MyMacro как:
#define my_macro(args) std::ostreamstring oss; \
oss << args; \
ThreadSafeLogging(oss.str());
Посмотрите google-glog, они делают это с использованием временного объекта, связанного с
LOG(INFO) << "log whatever" << 1;
и у них также есть другие интересные макросы, такие как LOG_IF и др.
Конечно, вы можете использовать его более одного раза:)!
Макрос __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;
}
Настройка ведения журнала, которую я имею, очень похожа:
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(), который одновременно сбрасывает его на выход журнала как одну команду и добавляет к нему новую строку, В моей реализации он всегда делает, но вы можете сделать это условным, если его еще нет.
Вот еще один неприятный трюк, который я видел где-то в другом месте. У этого есть существенный недостаток по сравнению с моим другим ответом: вы не можете использовать его дважды в той же области, потому что он объявляет переменную. Тем не менее, это может быть интересным для других случаев, когда вы хотите, чтобы 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;
}