Как перегрузить оператор шаблона+ для работы с const char* и const char[]?

Мой вопрос может быть открыт для интерпретации, поскольку я не уверен в конкретной ошибке, с которой столкнулся. Поэтому я буду признателен за любую идею или даже решение.

Проблема :

Я работаю над своим собственным классом Logger, основанным на репозитории this. Я внес много изменений, в том числе добавил overloaded operator+ для сборки/передачи входящих log_txt в std::map (K - thread_id, V - string_log).

Так:

  1. operator() - добавить время и статус в std::map;
  2. operator+ - ДОЛЖЕН собрать весь текст и значения и отправить результат string в std::map;
  3. 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()

К сожалению, операторы можно перегружать только для классов. Операторы не могут быть перегружены для собственных типов. Совет от профессионала: log(LOG_WARN) << "thread ONE : " << to_string(1111) << " val\n"; -- разве это не выглядит знакомо?

Sam Varshavchik 21.08.2023 14:30

При приоритет оператораa << b + c равен a << (b + c). Итак, вы используете встроенный const char* + int.

Jarod42 21.08.2023 14:33

обычно вы выбираете log(LOG_WARN) << "thread ONE : " << 1111 << " val\n";, что можно сделать, используя только перегрузку operator<<

463035818_is_not_an_ai 21.08.2023 14:42

каким должен быть результат log(LOG_WARN) << "thread ONE : " + to_string(1111) + " val\n";?

463035818_is_not_an_ai 21.08.2023 14:42

@SamVarshavchik - да, и я изначально использовал его, но я хотел получить больше контроля, разделив операции add_head/assemble/release на 3 dif. операторы. Насколько я помню, в этом есть логика (последовательность операторов ()->+-><<), так что мне просто интересно. @463035818_is_not_an_ai - лог [time][status]:thread ONE: 1111 val

Anton-Vas 21.08.2023 14:52

@SamVarshavchik - Забыл упомянуть: если я реализую 2 действия (assemble_msg и release) в operator<<, то его реализация будет выглядеть некрасиво. Но у меня появилась идея. Есть только этот вариант. Спасибо! @Тед Люнгмо - я читал об этом. Попытаюсь!

Anton-Vas 21.08.2023 15:07
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
6
73
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

Могу поспорить, что финал << нужен вам, чтобы сделать что-то особенное, и именно поэтому вы решили использовать оба 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);

«Смыв» не имеет особого значения. «освобождение» сработало бы слишком очевидно.

YSC 21.08.2023 15:24

Нет. Получить эту строку невозможно.

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++.

Я бы переписал требования так, чтобы

  1. operator() — получает потокобезопасным образом ссылку на std::string в std::map, уникальную для этой ветки, и добавляет к ней время и статус.
  2. operator<< — добавляется к std::string выше.
  3. В конце полного выражения при необходимости должны быть возможны дополнительные меры.

Дополнительными мерами может быть отправка записи журнала в центральный регистратор. В моем примере я просто добавляю \n, чтобы отметить конец записи журнала.

  • Здесь std::map блокируется только во время извлечения из него std::string. Тогда вход в отдельные std::string на карте можно будет выполнить без блокировки.
  • operator() возвращает прокси-объект (expr), который хранит ссылку на std::string в std::map
  • В конце полного выражения прокси-объект умирает и может затем выполнить любую необходимую дополнительную финализацию.
class 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;
};

Демо

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