Мой вопрос может быть открыт для интерпретации, поскольку я не уверен в конкретной ошибке, с которой столкнулся. Поэтому я буду признателен за любую идею или даже решение.
Я работаю над своим собственным классом Logger, основанным на репозитории this.
Я внес много изменений, в том числе добавил overloaded operator+ для сборки/передачи входящих log_txt в std::map (K - thread_id, V - string_log).
Так:
operator() - добавить время и статус в std::map;operator+ - ДОЛЖЕН собрать весь текст и значения и отправить результат string в std::map;operator<< - выкладывайте лог сразу, чтобы избежать текстовых конфликтов между логами из других тем.log(LOG_WARN) << "thread ONE : " + to_string(1111) + " val\n";
///> Will work but than i have to implement all possible types cast...
///> I'm trying to keep the code easy to read.
log(LOG_WARN) << "thread ONE : " + 1111 + " val\n";
///> Won't work --> error: invalid operands of types ‘const char*’ and ‘const char [6]’ to binary ‘operator+’
///> comment: "thread ONE : " + 1111 - const char* and + "val\n" - const char[6]
Мой overloaded operator+ как член класса Logger:
template <typename T>
Logger& Logger::operator+(const T& s){
lock_guard<mutex> lock(_mutex);
if (auto search = _th_streams.find(this_thread::get_id()); search != _th_streams.end()){
stringstream ss;
ss << s;
search->second.append(ss.str());
return *this;
}
return *this;
}
Я уверен, что моя реализация operator+ может быть далека от идеала, но мне интересно, можно ли устранить ошибку, отредактировав только мою operator+?
Перегрузить оператор+ или to_string()
При приоритет оператораa << b + c равен a << (b + c). Итак, вы используете встроенный const char* + int.
обычно вы выбираете log(LOG_WARN) << "thread ONE : " << 1111 << " val\n";, что можно сделать, используя только перегрузку operator<<
каким должен быть результат log(LOG_WARN) << "thread ONE : " + to_string(1111) + " val\n";?
@SamVarshavchik - да, и я изначально использовал его, но я хотел получить больше контроля, разделив операции add_head/assemble/release на 3 dif. операторы. Насколько я помню, в этом есть логика (последовательность операторов ()->+-><<), так что мне просто интересно. @463035818_is_not_an_ai - лог [time][status]:thread ONE: 1111 val
@SamVarshavchik - Забыл упомянуть: если я реализую 2 действия (assemble_msg и release) в operator<<, то его реализация будет выглядеть некрасиво. Но у меня появилась идея. Есть только этот вариант. Спасибо! @Тед Люнгмо - я читал об этом. Попытаюсь!





Могу поспорить, что финал << нужен вам, чтобы сделать что-то особенное, и именно поэтому вы решили использовать оба operator<< и operator+. Если это не так, этот ответ бесполезен, не обращайте на него внимания.
Вы можете пойти на:
log(LOG_WARN) << "thread ONE : " << to_string(1111) << " val\n" << Logger::flush;
Попросите Logger::operator<< сделать то, что вы Logger::operator+ делали раньше, но определите:
class Logger
{
struct flush_t {} flush;
};
Logger& Logger::operator<<(Logger::flush_t);
«Смыв» не имеет особого значения. «освобождение» сработало бы слишком очевидно.
Нет. Получить эту строку невозможно.
log(LOG_WARN) << "thread ONE : " + 1111 + " val\n";
работать, изменяя только вашу перегрузку operator+. Даже если вы тоже согласитесь изменить operator<<, эта строка не сможет сделать то, что вы намереваетесь.
Причина в том, что + имеет более высокий приоритет , чем +, и выражение сгруппировано следующим образом:
log(LOG_WARN) << (("thread ONE : " + 1111) + " val\n");
Нельзя перегружать operator+ для const char[] и int.
Есть несколько способов сделать это. Вы можете ввести специальные типы с соответствующей перегрузкой operator+, что-то вроде:
log(LOG_WARN) << X{"thread ONE : "} + Y{1111} + Z{" val\n"};
где X,Y и Z — это типы с operator+, реализованные соответствующим образом.
В качестве альтернативы рассмотрите возможность отказаться от operator+ и использовать operator<< до конца:
log(LOG_WARN) << "thread ONE : " + 1111 + " val\n";
Если вам нужно, чтобы последний << сделал что-то особенное, вы можете либо следовать тому, что предложил YSC в их ответе, либо заставить log(LOG_WARN) возвращать какой-нибудь легкий объект, который выполняет действие в своем деструкторе.
Из-за приоритета операторов вам придется что-то изменить в требованиях.
я бы пошел на
log(LOG_WARN) << "thread ONE : " << 1111 << " val";
который известен большинству программистов C++.
Я бы переписал требования так, чтобы
operator() — получает потокобезопасным образом ссылку на std::string в std::map, уникальную для этой ветки, и добавляет к ней время и статус.operator<< — добавляется к std::string выше.Дополнительными мерами может быть отправка записи журнала в центральный регистратор. В моем примере я просто добавляю \n, чтобы отметить конец записи журнала.
std::map блокируется только во время извлечения из него std::string. Тогда вход в отдельные std::string на карте можно будет выполнить без блокировки.operator() возвращает прокси-объект (expr), который хранит ссылку на std::string в std::mapclass Logger {
struct expr {
~expr() {
// the end of the full logging expression
th_stream += '\n';
}
template <class T>
expr& operator<<(const T& s) {
std::ostringstream ss;
ss << s;
th_stream.append(ss.str());
return *this;
}
std::string& th_stream;
};
public:
expr operator()(loglevel level) {
std::string& th_stream = [&]() -> std::string& {
std::lock_guard<std::mutex> lock(_mutex);
return _th_streams[std::this_thread::get_id()];
}();
th_stream += std::string("TIMESTAMP ") + std::to_string(level) + ' ';
return {th_stream};
}
private:
std::mutex _mutex;
std::map<std::thread::id, std::string> _th_streams;
};
К сожалению, операторы можно перегружать только для классов. Операторы не могут быть перегружены для собственных типов. Совет от профессионала:
log(LOG_WARN) << "thread ONE : " << to_string(1111) << " val\n";-- разве это не выглядит знакомо?