2015-10-27 4 views
2

Я хотел бы определить что-то вроде нового соиЬ, который я могу использовать для протоколирует данные:Перегрузка cout для ведения журнала?

some_type cout2(Message_type message){ 
    cout << message; 
    logfile.save(message); 
} 

поэтому я буду использовать его с

cout2 << "some message" << endl; 

До сих пор я не был в состоянии узнайте, как должен выглядеть код выше.

Благодарим за помощь.

+4

Возможно, у вас должен быть регистратор на основе экземпляра с помощью метода '.log()', который, в зависимости от реализации, может записывать в файл, писать на stdout и т. Д., А не пытаться перегрузить свободные функции из пространства имен std. Это будет намного больше OO и выглядеть немного дерьмо. –

+1

Как насчет использования ['std :: clog'] (http://www.cplusplus.com/reference/iostream/cout/) и перенаправления его вывода в файл: [' std :: cout.rdbuf (logfile.rdbuf());] (http://www.cplusplus.com/reference/ios/ios/rdbuf/)? – 865719

+0

Посмотрите, Натан, у меня также есть метод .log(), но я хотел бы знать, как выполнить то, что я описал выше, поэтому я поднял этот вопрос. Если бы я хотел знать, было ли это дерьмо или нет, я бы сформулировал это по-другому. – varantir

ответ

0

каротажных систем, которые я видел, построенный на этой идее выглядеть примерно так:

#define MY_PRINT(x, ...) \ 
     { \ 
      fprintf(logFile, x, ##__VA_ARGS__); \ 
      fflush(acuLogFile); \ 
     } 

И вы бы использовать его как:

MY_PRINT("this is just a test\n"); 

Несмотря на то, что это C способ делая вещи, он очень универсален и работает на C++.

Вы просто должны использовать свою новую функцию печати всюду в коде.

0

Возможно, у вас должен быть регистратор на основе экземпляра с методом .log(), который, в зависимости от реализации, может записывать в файл, писать на stdout и т. Д., А не пытаться перегрузить свободные функции из пространства имен std?

Ваше намерение будет более четким, и оно будет более ориентированным на объект.

Фактически, here is an example из регистратора событий для куки-файлов вы писали недавно, если это поможет вам понять, о чем я говорю. Мой дизайн позволяет осуществлять надёжность, а также принимает скучные решения о том, как что-то должно быть отформатировано как выходное и где оно должно идти (stdout или файл и т. Д.) В регистраторе.

2

Вы можете создать свой собственный регистратор как:

class Logger { 
public: 
    Logger(std::string const& filename) 
    : stream_(filename, std::ofstream::out | std::ofstream::app) 
    { 
    if (!stream_) { 
     // Error... 
    } 
    } 
    Logger& operator<<(std::string const& str) { 
    std::cout << str; 
    stream_ << str; 
    return *this; 
    } 
private: 
    std::ofstream stream_; 
}; 
1

Конечно вы можете определить свой собственный COUT. Сначала вам нужен класс, который может работать с оператором < < и содержит fstream и ostream (например, cout).

class logstream 
{ 
private: 
    fstream *filestream; 
    ostream *cout; 

public: 
    logstream(fstream* filestream, ostream* cout) 
    { 
     this->cout = cout; 
     this->filestream = filestream; 
    } 

    string operator<< (char* s) 
    { 
     *cout << s; 
     *filestream << s; 
     return s; 
    } 
}; 

Чтобы использовать это, просто инициализируйте его cout и fstream.

fstream lout = fstream("Filename", ios::app); 
logstream cout = logstream(&lout, &std::cout); 

И теперь у вас есть то, что вы хотели.

cout << "message"; 

EDIT: Не забудьте включить

#include <iostream> 
#include <fstream> 

и

using namespace std; 
+0

Я бы предложил не использовать «using namespace std», так как вам нужно заниматься скриптом между std :: cout и локальным cout. Я не думаю, что std :: директива здесь слишком длинная ... – varantir

2

Вообще говоря, классы из стандартной библиотеки C++ не предназначены для быть получены, и это верно для потоковых классов.

Так ИМХО, лучше указать, какой метод вы будете нуждаться в вашем cout2 объекта, а затем:

  • дизайн класс, содержащий ostream& объект, инициализируются в CTOR
  • Делегат фактический объем производства в том, что внутренний объект
  • делать все, что нужно войти вам в ваших методах

вы должны использовать шаблонный operator << к иметь возможность легко обрабатывать любой класс, который может обрабатывать std::ostream.

class LogStream { 
    std::ostream& out; 
    Logfile logfile; 

    LogStream(std::ostream& out, /* param for logfile initialization */ ...) 
     : out(out), logfile(...) {} 
    ... // other useful methods for state 
}; 
template<typename T> 
LogStream& operator << (LogStream& out, T val) { 
    out.out << message; 
    // should first test whether T is manipulator, and choose whether and how it should be logged 
    logfile.save(message); 
} 
2

Вы не хотите изменять std::cout.

Вместо этого вы хотите создать специализированный std::streambuf, который записывает два буфера, а не один. Например;

#include <streambuf> 
template <typename char_type, 
      typename traits = std::char_traits<char_type> > 
class basic_teebuf: 
    public std::basic_streambuf<char_type, traits> 
{ 
public: 
    typedef typename traits::int_type int_type; 

    basic_teebuf(std::basic_streambuf<char_type, traits> * sb1, 
       std::basic_streambuf<char_type, traits> * sb2) 
     : sb1(sb1) 
     , sb2(sb2) 
    { 
    } 

protected:  // override virtuals inherited from std::basic_streambuf 
    virtual int sync() 
    { 
     int const r1 = sb1->pubsync(); 
     int const r2 = sb2->pubsync(); 
     return r1 == 0 && r2 == 0 ? 0 : -1; 
    } 

    virtual int_type overflow(int_type c) 
    { 
     int_type const eof = traits::eof(); 

     if (traits::eq_int_type(c, eof)) 
     { 
      return traits::not_eof(c); 
     } 
     else 
     { 
      char_type const ch = traits::to_char_type(c); 
      int_type const r1 = sb1->sputc(ch); 
      int_type const r2 = sb2->sputc(ch); 

      return 
       traits::eq_int_type(r1, eof) || 
       traits::eq_int_type(r2, eof) ? eof : c; 
     } 
    } 

private: 
    std::basic_streambuf<char_type, traits> * sb1; 
    std::basic_streambuf<char_type, traits> * sb2; 
}; 

typedef basic_teebuf<char> teebuf; 

Затем вам нужно создать специализированный ostream, который использует такой буфер

#include <ostream> 
class teestream : public std::ostream 
{ 
public: 
    // Construct an ostream which tees output to the supplied 
    // ostreams. 
    teestream(std::ostream & o1, std::ostream & o2); 
private: 
    teebuf tbuf; 
}; 

teestream::teestream(std::ostream & o1, std::ostream & o2) 
    : std::ostream(&tbuf) 
    , tbuf(o1.rdbuf(), o2.rdbuf()) 
{ 
} 

Все вышеперечисленное делает это создание специализированного std::ostream, который использует наш специализированный буфер, который, в свою очередь, делает использование двух буферов ,

Теперь наш teestream необходимо инициализировать двумя потоками. Например

#include <fstream> 
#include <iostream> 

// include the preceding definition of teestream here 

int main() 
{ 
    std::ofstream logfile("hello-world.log"); 
    teestream tee(std::cout, logfile); 

     // tee is now a stream that writes the same output to std::cout and logfile 

    tee << "Hello, world!\n"; 
    return 0; 
} 

Преимущество этого в том, что все потоковых Вставки (оператор <<) будут работать с нашим teestream - даже для классов с перегруженными версиями.

Когда main() возвращается, потоки также будут закрыты чисто.

Я написал specalised streambuf в качестве шаблона (std::streambuf - это специализация шаблонного класса std::basic_streambuf). Для общности, вероятно, было бы лучше сделать то же самое с потоком (используя тот факт, что std::ostream также является специализацией шаблона std::basic_ostream). Я оставляю такое обобщение как упражнение.