2016-03-20 69 views
1

我有一個struct如何更換`{lock_guard鎖(obj.mut); obj.a_stringstream <<一個<< b /*...*/ << N;}`在方法調用

struct log { 
    boost::mutex mut; 
    std::stringstream a_stringstream; 
    //... 
    }; 

和在我的代碼我有那些的向量並經常做

{ 
    boost::lock_guard <boost::mutex> lock(logs[AN_ENUM].mut); 
    logs[AN_ENUM].a_stringstream << "something" << a_double << a_string << etc; 
} 

每當我想記錄一些東西時,似乎有詳細的鎖定守衛和括號。起初,我想也許我可以重載<<運算符爲我的結構,但我讀了它,我不認爲這可以工作。有沒有一種好的方法可以將這個方法壓縮成方法調用?

+0

一個典型的設置是使用'logs [AN_ENUM] << a << b << c;',其中第一個'<<'返回一個包含帶'<<'的析構函數完成的所有stringstream的對象工作。但是,霍華德建議使用函數調用語法的一個優點是,如果此消息低於當前級別,則可以實現日誌級別,然後不會在運行時浪費時間。 –

+0

@ M.M日誌級別?我無法想象析構函數會做什麼。如果你願意的話,擁有兩種解決方案都是非常好的。 –

+0

「日誌級別」意味着您可以爲日誌消息分配不同的優先級,然後在運行時(例如響應配置文件)決定顯示或不顯示。例如,您可能會記錄對調試有用的信息,但通常不會記錄該信息,除非有人遇到問題並決定將其打開。 –

回答

4

您可以創建一個可變參數模板函數(比如log_it),其採用AN_ENUM加項記錄的變量數,鎖定mutex一次,然後流一切a_stringstream。例如:

#include <mutex> 
#include <sstream> 
#include <vector> 

class save_stream 
{ 
    std::ostream&  os_; 
    char    fill_; 
    std::ios::fmtflags flags_; 
    std::streamsize precision_; 

public: 
    ~save_stream() 
    { 
     os_.fill(fill_); 
     os_.flags(flags_); 
     os_.precision(precision_); 
    } 

    save_stream(const save_stream&) = delete; 
    save_stream& operator=(const save_stream&) = delete; 

    explicit save_stream(std::ostream& os) 
     : os_(os) 
     , fill_(os.fill()) 
     , flags_(os.flags()) 
     , precision_(os.precision()) 
     {} 
}; 

struct log 
{ 
    std::mutex mut; 
    std::stringstream a_stringstream; 
}; 

std::vector<log> logs(10); 

enum : std::size_t {AN_ENUM}; 

void 
log_one(std::size_t) 
{ 
} 

template <class Arg0, class ...Args> 
void 
log_one(std::size_t log, Arg0 const& arg0, Args const& ...args) 
{ 
    logs[log].a_stringstream << arg0; 
    log_one(log, args...); 
} 

template <class ...Args> 
void 
log_it(std::size_t log, Args const& ...args) 
{ 
    std::lock_guard<std::mutex> lock(logs[log].mut); 
    save_stream s{logs[log].a_stringstream}; 
    logs[log].a_stringstream << "log " << log << " says : "; 
    log_one(log, args...); 
    logs[log].a_stringstream << '\n'; 
} 

這不是一個很大數量的代碼。 log_it取一個整數常量,用它來索引到logs來鎖定互斥鎖,吐出前綴字符串,然後調用log_one帶有常量日誌索引和可變參數數量註銷。

log_one然後簡單地記錄第一個參數,然後在包的其餘部分遞歸調用log_one

它可以像這樣使用:

log_it(AN_ENUM, std::fixed, std::setprecision(3), "i = ", 4.5); 
log_it(AN_ENUM,         "j = ", 4.5); 

導致logs[0].a_stringstream控股:

log 0 says : i = 4.500 
log 0 says : j = 4.5 

我敢肯定,語法可以巧妙的東西更漂亮,但得到的基本想法跨越:傳遞可變數量的參數,鎖定在調用堆棧的頂部,然後逐個處理每個參數。

「日誌級別」可能只是log_it的另一個參數。

0

爲什麼這個模板超載< <成員將無法工作,我想不出任何理由:

template<typename param> 
auto operator<<(param &&Param) 
{ 
    boost::lock_guard <boost::mutex> lock(mut); 

    a_stringstream << std::forward<param>(Param); 

    return *this; 
} 

我不知道你讀了什麼可能表明這種做法不會工作,但無論你讀什麼,都是錯誤的。我的例子可能需要一些調整,在這裏和那裏,取決於你的實際課程,但理論上它應該是非常可行的。

+2

這將釋放每個輸出值之間的鎖定,可能允許另一個線程在應該是連續輸出之間輸出一些東西(例如,'a_double'和OP的例子中的'a_string')。 – 1201ProgramAlarm

+0

你仍然可以做到這一點,但你將不得不做更多的工作。基本上來說:讓這個模板實例化一個包含鎖定保護的shared_ptr和一個指向它的指針。讓此模板返回shared_ptr,並將初始運算符<<參數轉發給它。在shared_ptr本身聲明第二個非成員運算符<<模板,其中包含我的示例中的有效內容。最終結果:該鎖被獲取一次,並持續整個<< s序列。如果您無法獲得非成員運算符<<正在處理shared_ptr,請將shared_ptr包裝到一個類中,然後在其上實現第二個運算符<<。 –

+0

...繼續。如果你的應用程序日誌很長,但性能可能會更糟,但仍然可行。你有沒有考慮過不使用operator <<,而是一個可變的log()函數,如:log(「something」,a_double,a_string)? –

相關問題