оператор << - как определить последний аргумент - PullRequest
5 голосов
/ 17 августа 2010

Я пишу класс журнала в C ++.Этот класс-одиночка.Я хочу добавить журналы таким образом:

Log::GetInstance() << "Error: " << err_code << ", in class foo";

Хорошо, и внутри объекта Log я хочу сохранить всю эту строку в тот момент, когда последний аргумент приходит (этот пример).

Как определить последний << аргумент?<< a << b << is_this_last << Maybe_this_is << or_not. </p>

Я не использую какие-либо конечные теги.

Ответы [ 6 ]

17 голосов
/ 17 августа 2010

Вы можете решить эту проблему, не используя синглтон. Если вы сделаете такую ​​функцию:

Log log()
{
    return Log();
}

Вы можете добавить журнал почти так же, как раньше:

log() << "Error: " << err_code << ", in class foo";

Разница в том, что деструктор объекта Log вызывается после этой строки. Теперь у вас есть способ определить, когда был обработан последний аргумент.

9 голосов
/ 17 августа 2010

Я бы попросил ваш Log::GetInstance вернуть прокси-объект вместо самого объекта журнала. Прокси-объект сохранит записанные в него данные, а затем в деструкторе фактически запишет накопленные данные в журнал.

4 голосов
/ 17 августа 2010

Я думаю, что Джерри и Мартин выдвинули лучшее предложение, но ради полноты первое, о чем я подумал, было: std::endl.

Если вы внедрили Log в системе iostreamпользовательским классом streambuf можно просто добавить << endl или << flush в конце строки.Поскольку вы спрашиваете, я полагаю, что вы этого не сделали.

Но вы можете имитировать, как работает endl.Либо добавьте обработчик манипулятора

Log &operator<< ( Log &l, Log & (*manip)( Log & ) )
    { return manip( l ); } // generically call any manipulator

Log &flog( Log &l ) // define a manipulator "flush log"
    { l->flush(); return l; }

, либо добавьте выделенный operator<<

struct Flog {} flog;

Log &operator<< ( Log &l, Flog )
    { l->flush(); return l; }
4 голосов
/ 17 августа 2010

Вы заставляете Log возвращать другой объект после оператора <<. </p>

template<typename T>
LogFindT operator<<(Log aLog, T const& data)
{
    // Put stuff in log.
    log.putStuffInLog(data);

    // now return the object to detect the end of the statement.
    return LogFindT(aLog);
}


struct LogFindT
{
    LogFindT(Log& aLog) : TheLog(aLog) {}
    Log& TheLog;
    ~LogFindT()
    {
        // Do stuff when this object is eventually destroyed
        // at the end of the expression.
    }
};

template<typename T>
LogFindT& operator<<(LogFindT& aLog, T const& data)
{
     aLog.TheLog.putStuffInLog(data);

     // Return a reference to the input so we can chain.
     // The object is thus not destroyed until the end of the stream.
     return aLog;
}
1 голос
/ 17 августа 2010

Не будьте слишком умны со своими операторами. Вы должны перегружать операторы, когда это имеет смысл. Здесь вы не должны. Это выглядит странно.

У вас должен быть статический метод, который выглядит следующим образом:

Log::Message( message_here );

, который принимает std :: string. После этого у клиентов возникают головные боли, чтобы понять, как собрать строку ошибки.

0 голосов
/ 17 августа 2010

Нет хорошего способа сделать то, что вы хотите. C и C ++ просто не ориентированы на строки. Нет более крупной единицы, такой как «строка кода» или чего-либо еще, и никакие цепные вызовы не объединяются каким-либо образом.

В C ++ выражение «a << b << c << d» в точности эквивалентно трем отдельным вызовам оператора <<, например: </p>

 t1 = a;
 t2 = t1.operator<<(b);
 t3 = t2.operator<<(c);
 t4 = t3.operator<<(d);

Вот почему C ++ ostreams использует endl в качестве явного маркера конца строки; просто нет приличного способа сделать это иначе.

...