Проблема с манипулятором C ++ - PullRequest
0 голосов
/ 16 ноября 2009

Я пытаюсь реализовать свой собственный потоковый манипулятор в своем классе логирования. Это в основном манипулятор конца строки, который изменяет состояние флага. Однако, когда я попытаюсь использовать его, я получу:

ftypes.cpp:57: error: no match for ‘operator<<’ in ‘log->Log::debug() << log->Log::endl’
/usr/lib/gcc/i386-redhat-linux/4.1.2/../../../../include/c++/4.1.2/bits/ostream.tcc:67: note: candidates are: std::basic_ostream<_CharT, _Traits>& std::basic_ostream<_CharT, _Traits>::operator<<(std::basic_ostream<_CharT, _Traits>& (*)(std::basic_ostream<_CharT, _Traits>&)) [with _CharT = char, _Traits = std::char_traits<char>]
/usr/lib/gcc/i386-redhat-linux/4.1.2/../../../../include/c++/4.1.2/bits/ostream.tcc:78: note:                 std::basic_ostream<_CharT, _Traits>& std::basic_ostream<_CharT, _Traits>::operator<<(std::basic_ios<_CharT, _Traits>& (*)(std::basic_ios<_CharT, _Traits>&)) [with _CharT = char, _Traits = std::char_traits<char>]
/usr/lib/gcc/i386-redhat-linux/4.1.2/../../../../include/c++/4.1.2/bits/ostream.tcc:90: note:                 std::basic_ostream<_CharT, _Traits>& std::basic_ostream<_CharT, _Traits>::operator<<(std::ios_base& (*)(std::ios_base&)) [with _CharT = char, _Traits = std::char_traits<char>]

...

Код:

class Log {
public:
  ...
  std::ostream& debug() { return log(logDEBUG); }  
  std::ostream& endl(std::ostream& out);           // manipulator
  ...
private:
  ...
  std::ofstream m_logstream;
  bool          m_newLine;
  ...
}


std::ostream& Log::endl(std::ostream& out) 
{  
  out << std::endl;
  m_newLine = true;
  return out;
}

std::ostream& Log::log(const TLogLevel level)
{
  if (level > m_logLevel) return m_nullstream;

  if (m_newLine)
  {
    m_logstream << timestamp() << "|" << logLevelString(level) << "|";
    m_newLine = false;
  }
  return m_logstream;
}

Я получаю сообщение об ошибке при попытке вызвать его:

log->debug() << "START - object created" << log->endl;

(log - указатель на объект Log)

Есть идеи? Я подозреваю, что это как-то связано с тем фактом, что манипулятор на самом деле находится внутри класса, но это только мое дикое предположение ...

Приветствия

Tom

РЕДАКТИРОВАТЬ: Поместить это здесь вместо комментария из-за ограничения форматирования. Я попытался реализовать свой streambuf, и он прекрасно работает с одним исключением: когда я пытаюсь открыть filebuf для добавления, это не удается. Вывод работает хорошо, просто добавить не по неизвестной причине. Если я пытаюсь использовать ofstream напрямую с append, это работает. Есть идеи почему? - Работает:

std::ofstream test; 
test.open("somefile", std::ios_base::app); 
if (!test) throw LogIoEx("Cannon open file for logging"); 
test << "test" << std::endl;

Добавляет «test» правильно.

Не работает:

std::filebuf *fbuf = new std::filebuf(); 
if (!fbuf->open("somefile", std::ios_base::app)) throw LogIoEx("Cannon open file for logging"); 

Выдает исключение, если я установил openmode на out, то это сработает ..

Приветствия

Ответы [ 3 ]

5 голосов
/ 16 ноября 2009

Определяется operator<<(ostream &, ostream &(*)(ostream&)), но не operator<<(ostream &, ostream &(Log::*)(ostream&)). То есть манипулятор работал бы, если бы это была обычная функция (не являющаяся членом), но, поскольку он зависит от экземпляра Log, обычная перегрузка не сработает.

Чтобы решить эту проблему, вам может потребоваться, чтобы log->endl был экземпляром вспомогательного объекта, и при нажатии operator<< вызовите соответствующий код.

Вот так:

class Log {
  class ManipulationHelper {  // bad name for the class...
  public:
    typedef ostream &(Log::*ManipulatorPointer)(ostream &);

    ManipulationHelper(Log *logger, ManipulatorPointer func) :
      logger(logger),
      func(func) {
    }

    friend ostream &operator<<(ostream &stream, ManipulationHelper helper) {
        // call func on logger
        return (helper.logger)->*(helper.func)(stream);
    }

    Log *logger;
    ManipulatorPointer func;
  }

  friend class ManipulationHelper;

public:
  // ...

  ManipulationHelper endl;

private:
  // ...

  std::ostream& make_endl(std::ostream& out); // renamed
};

// ...

Log::Log(...) {
  // ...
  endl(this, make_endl) {
  // ...
}
2 голосов
/ 16 ноября 2009

Манипуляторы работают не так - все дело в типах. То, что вы хотите, это что-то вроде:

class Log {
...
struct endl_tag { /* tag struct; no members */ };
static const struct endl_tag endl;
...
LogStream &debug() { /* somehow produce a LogStream type here */ }
}

LogStream &operator<<(LogStream &s, const struct endl_tag &) {
  s.m_newLine = true;
}

Обратите внимание, что:

  1. Поскольку m_newLine является частью Log, мы не можем работать с общими std::ostream s. В конце концов, что бы означало std::cout << Log->endl()? Поэтому вам нужно создать новый тип потока, полученный из std::ostream (я оставил его здесь, но предположил, что он называется LogStream).
  2. endl на самом деле ничего не делает сам; вся работа в operator<<. Единственная цель этого - заставить работать правильную перегрузку operator<<.

Тем не менее, вы не должны определять новые манипуляторы и потоковые классы, если вы можете избежать этого, потому что это становится сложным :) Можете ли вы делать то, что вам нужно, используя std::endl и оборачивая ostream свой собственный streambuf? Вот как предполагается использовать библиотеку ввода-вывода C ++.

1 голос
/ 16 ноября 2009

Попробуйте это:

#include <iostream>

class Log
{
    public:
    class LogEndl
    {
        /*
         * A class for manipulating a stream that is associated with a log.
         */
        public:
            LogEndl(Log& p)
                :parent(p)
            {}
        private:
            friend std::ostream& operator<<(std::ostream& str,Log::LogEndl const& end);
            Log&    parent;
    };
    std::ostream& debug()   {return std::cout;}
    /*
     * You are not quite using manipulators the way they are entended.
     * But I wanted to give an example that was close to your original
     *
     * So return an object that has an operator << that knows what to do.
     * To feed back info to the Log it need to keep track of who its daddy is.
     */
    LogEndl       endl()    {return LogEndl(*this);}
    private:
        friend std::ostream& operator<<(std::ostream& str,Log::LogEndl const& end);
        bool    endOfLine;

};


std::ostream& operator<<(std::ostream& str,Log::LogEndl const& end)
{
    // Stick stuff on the stream here.
    str << std::endl;

    // Make any notes you need in the log class here.
    end.parent.endOfLine    = true;

    return str;
};

int main()
{
    Log     log;

    /*
     * Please note the use of objects rather than pointers here
     * It may help
     */
    log.debug() << "Debug " << log.endl();
}
...