2013-07-21 24 views
6

Tôi đã tạo đối tượng giống như std::cout giống như viết cả std::cout và vào tệp nhật ký.Cách chính xác để khai báo/xác định đối tượng cout tùy chỉnh giống như

Tôi hiện đang xác định nó như thế này trong tệp tiêu đề, nhưng tôi nhận được cảnh báo biến không sử dụng.

tập tin header <MyLib/Log.h>

static LOut { }; 
static LOut lo; 

template<typename T> inline LOut& operator<<(LOut& mLOut, const T& mValue) 
{ 
    std::string str{toStr(mValue)}; 
    std::cout << str; 
    getLogStream() << str; 
    return mLOut; 
} 

Cách sử dụng:

#include <MyLib/Log.h> 
... 
lo << "hello!" << std::endl; 

nên lo được static? Nên loextern?

Các yêu cầu để giải thích đúng cách khai báo đối tượng giống như cout và hiển thị cách triển khai thư viện chuẩn chính thực hiện.


Chỉnh sửa: bởi cout giống như đối tượng, tôi có nghĩa là biến toàn cục luôn có sẵn sau khi bao gồm tiêu đề tương ứng.

+0

'std :: cout' thường chỉ là một' std :: ostream' của một số loại, với một số đặc biệt logic để đảm bảo rằng nó được khởi tạo sớm, và không bao giờ bị phá hủy; ít nhất một trình biên dịch tôi biết sử dụng các phần mở rộng đặc biệt để đạt được điều này. Nhưng bạn thường không cần nó; nếu tồi tệ hơn đến tồi tệ hơn, bạn có thể sử dụng một singleton, và viết 'log() << ...'. –

Trả lời

5

std::cout chỉ đơn giản là khai báo như sau:

namespace std { 
    extern ostream cout; 
} 

Nó là một biến toàn cầu thường xuyên; bạn có thể tự làm điều tương tự. Đặt một tuyên bố extern biến của bạn trong tiêu đề; sau đó xác định các biến tương tự trong một file nguồn và liên kết nó với ứng dụng của bạn:

// mylog.h 
extern MyLog mylog; 

// mylog.cpp 
MyLog mylog(someparams); 
+0

Cảm ơn. Tôi đã cố gắng tuyên bố nó 'bên ngoài' nhưng quên để xác định biến trong tập tin nguồn. –

1

Trước tiên, tôi không chắc chắn ý của bạn là đối tượng giống như đối tượng cout? Có thể là std::ostream.

Dù sao, cách thông thường để thực hiện việc này là sử dụng lọc streambuf. Chỉ cần viết một streambuf đó chuyển tiếp vào một tập tin nhật ký, ngoài chỗ cũ nhé, và chèn nó có bao giờ bạn muốn :

class LoggingOutputStreambuf : public std::streambuf 
{ 
    std::streambuf* myDest; 
    std::ofstreambuf myLogFile; 
    std::ostream* myOwner; 
protected: 
    int overflow(int ch) 
    { 
     myLogFile.sputc(ch); // ignores errors... 
     return myDest->sputc(ch); 
    } 
public: 
    LoggingOutputStreambuf(
      std::streambuf* dest, 
      std::string const& logfileName) 
     : myDest(dest) 
     , myLogFile(logfileName.c_str(), std::ios_base::out) 
     , myOwner(nullptr) 
    { 
     if (!myLogFile.is_open()) { 
      // Some error handling... 
     } 
    } 
    LoggingOutputStreambuf(
      std::ostream& dest, 
      std::string const& logfileName) 
     : LoggingOutputStreambuf(dest.rdbuf(), logfileName) 
    { 
     dest.rdbuf(this); 
     myOwner = &dest; 
    } 
    ~LoggingOutputStreambuf() 
    { 
     if (myOwner != nullptr) { 
      myOwner->rdbuf(myDest); 
     } 
    } 
}; 

(Đây là C++ 11, nhưng nó không phải là khó khăn . để sửa đổi nó cho C++ 03)

để sử dụng, bạn có thể sử dụng một cái gì đó như:

LoggingOutputStreambuf logger(std::cout); 
// ... 

Tất cả các đầu ra để std::cout sẽ được đăng nhập cho đến khi logger đi ra ngoài phạm vi.

Trong thực tế, bạn sẽ có khả năng sử dụng một cái gì đó phức tạp hơn một filebuf cho khai thác gỗ, kể từ khi bạn có thể muốn để chèn thời gian tem vào lúc bắt đầu của mỗi dòng, hoặc có hệ thống xả nước vào cuối mỗi dòng. (Lọc streambufs cũng có thể xử lý các vấn đề đó .)

+0

Cảm ơn, nhưng tôi mong muốn một biến toàn cục luôn sẵn có, đó là lý do tại sao tôi đã nói về đối tượng giống như 'cout' –

+0

Vì vậy, bạn quan tâm đến cuộc đời. Điều đó không rõ ràng. Trong trường hợp đó, giải pháp hiển nhiên là 'std :: ostream & logStream() {static std :: ostream * theOneAndOnly = new MyStreamType; return * theOneAndOnly; } 'Có một thay đổi nhỏ trong cú pháp: bạn phải viết' logStream() << ... ', thay vì' logStream << ... ', nhưng đó là về nó. (Và bạn vẫn có thể sử dụng 'LoggingOutputStreambuf' ở trên trong luồng bạn trở về từ' logStream'; nó vẫn là cách đơn giản và thanh lịch nhất để đẩy đầu ra tới hai đích riêng biệt.) –

0

Trong một trong số projects, tôi đã viết trình bao bọc cho std::cout.

Nó trông giống như sau:

struct out_t { 
    template<typename T> 
    out_t& 
    operator << (T&& x) { 
      std::cout << x; 
      // log << x; 
      return *this; 
    }; 
}; 

out_t out; 

out << 1; 

Đối với hoàn nhìn mã cho struct out trong io.h

+0

Điều này gần hơn với những gì tôi muốn - tuy nhiên tôi muốn một thể hiện toàn cục có thể được sử dụng mà không có toán tử(), giống như 'cout'. –

+0

Không có gì để bạn khai báo đối tượng 'out' toàn cục. Tôi đã chỉnh sửa mã để hiển thị cách thực hiện. –

+0

@LeonidVolnitsky Nếu tôi không sử dụng C++ 11, có thể thay thế '&&' bằng '&' trong đoạn mã bạn đã hiển thị không? – synaptik

1

std :: cout đối tượng giống như mà viết cả std :: cout và một bản ghi file

Có lẽ boost.iostreams là đủ?

#include <iostream> 
#include <fstream> 
#include <boost/iostreams/stream.hpp> 
#include <boost/iostreams/tee.hpp> 

namespace io = boost::iostreams; 
int main() 
{ 
    typedef io::tee_device<std::ostream, std::ofstream> teedev; 
    typedef io::stream<teedev> LOut; 
    std::ofstream outfile("test.txt"); 
    teedev logtee(std::cout, outfile); 
    LOut mLOut(logtee); 
    mLOut << "hello!" << std::endl; 
} 
+0

Tôi không muốn giới thiệu bất kỳ sự phụ thuộc tăng nào, và tôi muốn một biến toàn cục như 'cout' –

1

Đơn giản chỉ cần gửi các giá trị đầu vào đúng ra cout không làm việc cho tôi, vì tôi muốn thêm tiêu đề và thông tin để đăng nhập đầu ra.

Ngoài ra, tôi đã có lớp gỡ lỗi tĩnh của mình để bao bọc luồng nhật ký.

Đây là cách tôi quản lý để làm điều này, tôi hy vọng nó hữu ích. Tôi bằng cách nào đó một lời tạm biệt với c + +, do đó, cảm thấy tự do để cho tôi biết nếu một cái gì đó là sai :)

#include <iostream> 
#include <sstream> 
#include <ostream> 

enum class DebugLevel { 
    INFO, 
    WARNING, 
    ERROR 
}; 

class Debug { 

    public: 

     /* other Debug class methods/properties 
      ... 
     */ 

     // out stream object 
     static struct OutStream { 

       std::ostringstream stream; 
       DebugLevel level = DebugLevel::INFO; 

      public: 

       // here you can add parameters to the object, every line log 
       OutStream& operator()(DebugLevel l) { 
        level = l; 
        return *this; 
       } 

       // this overload receive the single values to append via << 
       template<typename T> 
       OutStream& operator<<(T&& value) { 
        stream << value; 
        return *this; 
       } 

       // this overload intercept std::endl, to flush the stream and send all to std::cout 
       OutStream& operator<<(std::ostream& (*os)(std::ostream&)) { 

        // here you can build the real console log line, add colors and infos, or even write out to a log file 
        std::cout << __TIME__ << " [" << (int)level << "] " << stream.str() << os; 

        stream.str(""); // reset the string stream 
        level = DebugLevel::INFO; // reset the level to info 
        return *this; 
       } 

     } Log; 

}; 

Debug::OutStream Debug::Log; // need to be instantiaded only because I use a static Debug class 

int main() { 

    Debug::Log(DebugLevel::ERROR) << "Hello Log! " << 2 << " " << __FUNCTION__ << std::endl; 

    Debug::Log << "Hello Log! " << 0xFA << std::endl; // NB: this way the debugLevel is default 

    return 0; 

} 
Các vấn đề liên quan