2009-04-17 34 views
30

Tôi đang xem xét việc tạo lớp ghi nhật ký có các thành viên như Thông tin, Lỗi, vv có thể cấu hình đầu ra thành bàn điều khiển, tệp hoặc không đến đâu.Triển khai thực hiện lệnh không hoạt động :: ostream

Để đạt hiệu quả, tôi muốn tránh phí trên của việc định dạng thư sẽ bị vứt bỏ (tức là thông báo thông tin khi không chạy ở chế độ tiết). Nếu tôi thực hiện một tùy chỉnh std :: streambuf mà kết quả đầu ra đến hư không, tôi tưởng tượng rằng std :: ostream lớp vẫn sẽ làm tất cả các định dạng. Bất cứ ai có thể đề nghị một cách để có một thực sự "null" std :: ostream rằng tránh làm bất kỳ công việc ở tất cả các thông số được truyền cho nó với <<?

Cảm ơn.

+0

tôi sẽ không lo lắng. chỉ cần sử dụng một dòng null như được hiển thị bởi neil. các lớp học không cần bất kỳ hiệu suất tốt hơn, bởi vì rõ ràng nếu bạn không có một mục tiêu null, định dạng * có * được thực hiện, vì vậy nó rõ ràng là không quan trọng. chỉ là 2 xu của tôi –

+0

hmm, nhưng có vẻ như nó được dự định là một "đầu ra gỡ lỗi" thingy? một cách tôi đã thấy là như thế này: out() << a << b ...; và out() trả về struct f {}; với mẫu là f const & toán tử << (f const & f_, T const) {return f_; }, và sau đó tạo ra các cấu trúc khác nhau tùy thuộc vào cấp độ nhật ký. hoặc thực hiện các chức năng khác nhau hoặc bất kỳ chức năng nào. –

Trả lời

4

Để ngăn các lời gọi operator<<() thực hiện định dạng, bạn nên biết kiểu luồng tại thời gian biên dịch. Điều này có thể được thực hiện bằng macro hoặc với các mẫu.

Giải pháp mẫu của tôi tuân theo.

class NullStream { 
public: 
    void setFile() { /* no-op */ } 
    template<typename TPrintable> 
    NullStream& operator<<(TPrintable const&) 
    { /* no-op */ } 
} 

template<class TErrorStream> // add TInfoStream etc 
class Logger { 
public: 
    TErrorStream& errorStream() { 
     return m_errorStream; 
    } 

private: 
    TErrorStream m_errorStream; 
}; 

//usage 
int main() { 
    Logger<std::ofstream> normal_logger; // does real output 
    normal_logger.errorStream().open("out.txt"); 
    normal_logger.errorStream() << "My age is " << 19; 

    Logger<NullStream> null_logger; // does zero output with zero overhead 
    null_logger.errorStream().open("out.txt"); // no-op 
    null_logger.errorStream() << "My age is " << 19; // no-op 
} 

Vì bạn phải làm điều này vào thời gian biên dịch, tất nhiên là nó không linh hoạt.

Ví dụ: bạn không thể quyết định mức ghi nhật ký khi chạy từ tệp cấu hình.

+0

+1: đơn giản, sạch sẽ, thực hiện tốt công việc. Lưu ý rằng ví dụ:"NullStream s; s << expensive_function();" rất có thể vẫn sẽ đánh giá hàm đắt_function(), đặc biệt nếu nó nằm trong một mô-đun khác. –

+3

Tôi cũng sẽ đề cập rằng, không giống như của Neil onullstream, NullStream của bạn không thể được chuyển đến một chức năng mong đợi một ostream & hoặc ostream * đối số. –

+0

expensive_function() sẽ _definitely_ được đánh giá bất kể nó ở đâu. Không có cách nào để pervent rằng các macro chặn và biên dịch có điều kiện :) ... như để onullstream Neil, tôi không tin rằng nó đáp ứng các yêu cầu "không định dạng trên không" :) –

0

Có thể bạn sẽ cần nhiều hơn là định dạng văn bản và lọc thư. Điều gì về đa luồng?

Tôi sẽ triển khai đồng bộ lọc và đa luồng như trách nhiệm của một lớp riêng biệt.

Tuy nhiên, ghi nhật ký là một vấn đề không đơn giản và tôi sẽ cố gắng sử dụng các giải pháp ghi nhật ký hiện có thay vì phát triển một giải pháp đăng nhập mới.

15

Google nhanh chóng đưa ra ví dụ này có thể được sử dụng. Tôi không bảo đảm, ngoại trừ việc nó biên dịch và chạy :-)

#include <streambuf> 
#include <ostream> 

template <class cT, class traits = std::char_traits<cT> > 
class basic_nullbuf: public std::basic_streambuf<cT, traits> { 
    typename traits::int_type overflow(typename traits::int_type c) 
    { 
     return traits::not_eof(c); // indicate success 
    } 
}; 

template <class cT, class traits = std::char_traits<cT> > 
class basic_onullstream: public std::basic_ostream<cT, traits> { 
    public: 
     basic_onullstream(): 
     std::basic_ios<cT, traits>(&m_sbuf), 
     std::basic_ostream<cT, traits>(&m_sbuf) 
     { 
      init(&m_sbuf); 
     } 

    private: 
     basic_nullbuf<cT, traits> m_sbuf; 
}; 

typedef basic_onullstream<char> onullstream; 
typedef basic_onullstream<wchar_t> wonullstream; 

int main() { 
    onullstream os; 
    os << 666; 
} 
+0

+1. Có, tôi đoán rằng bắt nguồn từ std :: basic_ostream <> là cần thiết nếu bạn muốn truyền một luồng không làm gì vào một hàm mong đợi một tham số ostream và hoặc ostream * - thủ thuật của Iraimbilanja sẽ không hoạt động ở đó. –

+0

nó chỉ có thể được chấp nhận là 'ostream *', không phải 'ostream &', đúng không? – athos

0

Tại sao không sử dụng các giải pháp ghi nhật ký hiện có được hàng triệu người dùng sử dụng? log4j, log4net, log4cxx .., chỉ cần một vài tên ..

13

tất cả, cảm ơn vì chia sẻ mã, tôi chỉ làm một bài kiểm tra, sau đó phương pháp Neil sẽ vẫn làm chuỗi Formating, ví dụ:

#include <streambuf> 
#include <ostream> 
#include <iostream> 
using namespace std; 


template <class cT, class traits = std::char_traits<cT> > 
class basic_nullbuf: public std::basic_streambuf<cT, traits> { 
    typename traits::int_type overflow(typename traits::int_type c) 
    { 
     return traits::not_eof(c); // indicate success 
    } 
}; 

template <class cT, class traits = std::char_traits<cT> > 
class basic_onullstream: public std::basic_ostream<cT, traits> { 
    public: 
     basic_onullstream(): 
     std::basic_ios<cT, traits>(&m_sbuf), 
     std::basic_ostream<cT, traits>(&m_sbuf) 
     { 
      init(&m_sbuf); 
     } 

    private: 
     basic_nullbuf<cT, traits> m_sbuf; 
}; 

typedef basic_onullstream<char> onullstream; 
typedef basic_onullstream<wchar_t> wonullstream; 

class MyClass 
{ 
    int a; 
    friend ostream& operator<< (ostream&, MyClass const&); 
}; 

ostream& operator<<(ostream& out,MyClass const& b) 
{ 
    std::cout<<"call format function!!"; 
    out << b.a; 
    return out; 
} 

int main() { 
    onullstream os; 
    MyClass obj; 
    os<<obj; 
} 

Chạy chương trình này, bạn sẽ thấy rằng "ostream & hành < < (ostream & ra, MyClass const & b)" sẽ được gọi. Vì vậy, làm định dạng trên obj vẫn sẽ được gọi. Vì vậy, chúng tôi vẫn không thể tránh được các thông báo định dạng.

+0

AFAIK trình tối ưu hóa có thể xóa mã được cho là không có tác dụng phụ. Vì lệnh gọi hàm 'std :: cout <<" gọi hàm !! "' không có tác dụng phụ, thì trình tối ưu hóa sẽ không loại bỏ điều này. Tuy nhiên, không có cuộc gọi, có thể điều này sẽ bị xóa –

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