Пользовательский манипулятор потока для класса

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

class CAudit
{
public:
    //needs to be templated
    CAudit& operator << ( LPCSTR data ) {
        audittext << data;
        return *this;
    }

    //attempted manipulator
    static CAudit& write(CAudit& audit) { 
        //write contents of audittext to audit and clear it
        return audit; 
    }

private:
    std::stringstream audittext;
};

//to be used like
CAudit audit;
audit << "Data " << data << " received at " << time << CAudit::write;

Я понимаю, что перегруженный оператор в моем коде не возвращает объект потока, но задавался вопросом, возможно ли еще использовать синтаксис манипулятора. В настоящее время компилятор видит '< < как двоичный оператор сдвига вправо.

Спасибо за любой вклад, Патрик

Ответ 1

Чтобы заставить его работать, вы должны добавить перегрузку оператора < < для функций, чем вызов функции из него:

 class CAudit
 {
  //...other details here as in original question

  CAudit& operator << (CAudit & (*func)(CAudit &))
  {
        return func(*this);
  }
 };

 CAudit audit;
 audit << "some text" << CAudit::write;

Ответ 2

Оператор двоичного сдвига и оператор потока - это тот же самый оператор. Полностью законно перегружать operator + для вашего класса, чтобы написать "Hello world" на std:: cout (хотя это очень плохая идея). Точно так же авторы стандарта С++ решили перегрузить оператор < для потоков как запись в поток.
Вы четко не писали, в чем проблема. Мое предположение - ошибка компиляции. В этом случае лучше всего привести сообщение об ошибке. Если я прав, проблема в том, что вы определили только оператор < < для LPCSTR, а затем вы хотите, чтобы он работал с функциональным объектом с правой стороны.
Вы используете слово "манипулятор", но вы ничего не понимаете. Манипулятор для потока (поток из STL) - это функция, которая выполняет некоторые действия над потоком, в который он записывается. И он работает только из-за этой перегрузки:

ostream& operator<< (ostream& ( *pf )(ostream&));

который принимает функцию и применяет ее к потоку.
Аналогично вам нужно:

CAudit& operator<< (CAudit& ( *pf )(CAudit& audit))
{
  return (*pf)(audit);
}

Ответ 3

Не будет ли это

class CAudit
{
public:
    template< typename T >
    CAudit& operator<<( const T& data )
    {
        audittext << data;
        return *this;
    }

    class write {};

    void operator<<( const write& data )
    {
        /* whatever */
    }

private:
    std::stringstream audittext;
};

делай, что хочешь?

Ответ 4

Я делаю что-то очень похожее на трассировку, но использую stringstream. Это гарантирует, что все сторонние operator << () и манипуляторы работают. Я также использую дескриптор вместо манипулятора записи клиента.

class DebugStream
{
public:
    DebugStream(short level, const char * file, int line) {
        sstream << "L" << level << "\t" << file << "\t" << line << "\t";
    }
    ~DebugStream() { write(sstream.str()); }

    std::ostream & stream() { return sstream; }
private:
    std::stringstream sstream;

    DebugStream(const DebugStream &);
    DebugStream & operator=(const DebugStream &);
};

Затем это делается с некоторыми макросами:

#define DBG_ERROR if (1<=dbg_level()) DebugStream(1, __FILE__, __LINE__).stream()
#define DBG_INFO  if (2<=dbg_level()) DebugStream(2, __FILE__, __LINE__).stream()

И код просто использует макросы

DBG_INFO << "print some debug information";

Вам не нужен конкретный манипулятор записи, чтобы очистить данные до файла журнала. Когда анонимный объект DebugStream выходит за рамки (после того, как элемент управления покидает строку), содержимое автоматически записывается.

Хотя я обычно избегаю макросов, в этом случае использование оператора if означает, что у вас нет накладных расходов на построение линии трассировки, если вы на самом деле этого не требуете.

Возврат ostream с помощью метода stream() позволяет этому работать для глобальных функций-членов, поскольку анонимные объекты не могут передаваться как неконстантные ссылочные параметры.