簡體   English   中英

如何在 c++ 的多個位置訪問 class 的模板化 static 變量

[英]How to access templatized static variable of a class in multiple places in c++

我正在 c++ 中開發一個功能模擬器項目。 我的目標是將最后 50 個調試日志語句存儲在滾動緩沖區中,並在模擬器中出現錯誤時顯示它們。 這將有助於更快地調試。 否則,當出現錯誤時,我將不得不記下周期,並在啟用日志的情況下重新運行模擬器,這既費時又慢。

在文件 abc.h 日志結構中存儲調試日志語句、生成日志的模擬周期等

template <typename T, typename... U>
struct Logging {
    ......
}

abc.h 中的以下模板化 class 包含一個 static 成員,它是滾動的 window 包含最后 50 個日志調試語句

template <typename T> 
class LogGlobal {
   public:    
       static std::deque<T> last_50_logs; //rolling window of last 50 log statements

};
template <typename T> std::deque<T> LogGlobal<T>::last_50_logs = {};

在 abc.h 中的 debug_log function 內部,我將一些對象推送到 static 變量,如下所示:

template <typename T, typename... U>
void debug_log(T&& t, U&&... u) const
{
    //other code to ensure that this deque holds last 50 log statements only is not written here for simplicity
    LogGlobal<Logging<T, U...>>::last_50_logs.push_back(<Logging<T, U...>{...}); 
}

在 abc.cpp 文件中,我們在處理 function 時遇到錯誤,只要模擬器中出現錯誤,就會調用該錯誤。

void error_handler()
{
    //I want to access the last_50_logs static variable here but I don't have information about the template T and U here. How do I solve this?
} 

在 error_handler function 中,我無法訪問用於創建 LogGlobal class 的模板類型。 有沒有辦法或解決方法來了解這些細節?

解決此問題的一種方法是,每當我推送到 last_50_logs 時,我可以將日志語句存儲為 std::string 而不是模板 T 和 U 內的 Logging 結構。 但這真的很慢,因為字符串操作很昂貴。

解決此問題的一種方法是,每當我推送到 last_50_logs 時,我都可以將日志語句存儲為 std::string 而不是在 Logging 結構中的模板 T 和 U。 但這真的很慢,因為字符串操作很昂貴。

好吧,您不能將日志事件存儲為某種未知類型T ,因為......那么您不知道該類型是什么。

但是,您可以使用某種類型的擦除來推遲格式化,直到(除非)您確實需要該字符串。 例如,您可以在雙端隊列中存儲std::function<std::string()> object,

using DeferredLog = std::function<std::string()>;
class LogGlobal {
   public:    
       static std::deque<DeferredLog> last_50_logs; //rolling window of last 50 log statements
};

template <typename T, typename... U>
void debug_log(T&& t, U&&... u) const
{
    // blah blah blah circular buffer
    LogGlobal<Logging<T, U...>>::last_50_logs.push_back(
      [t, u...] () { return format_log(t, u...); }
    ); 
}

請注意,類型擦除確實有一些開銷,並且所有 arguments 都需要按值捕獲,因此不能保證比僅格式化固定寬度的字符串更快。 對其進行基准測試並查看。 如果看起來值得的話,您總是可以手動滾動一些不如std::function和 lambda 捕獲通用的東西。

每個日志消息的不同類型,就像在你的例子中一樣,太麻煩和不方便,不實用。

日志通常是文本,因此一個自然的解決方案是為您的日志消息使用std::string的循環緩沖區。 您將消息格式化為std::string並將其存儲到緩沖區中。

對於結構化日志記錄,您的日志記錄必須有另一個特定的(基本)class。


如果您不想在消息為 output 之前對其進行格式化,請使用格式化函數的環形緩沖區來復制日志消息 arguments。

每種格式 function 是一個 lambda 閉包 object 與日志消息 ZDBC11CAA5BDA99F77E6FB4DABD882E 捕獲的值。 至少,通過價值來防止引用變得懸空。 您仍然可以傳遞一個稍后可能會懸空的指針,但可以在編譯時拒絕指針 arguments(除非它們指向編譯時常量,具有非標准編譯器擴展。)。

一個完整的例子:

#include <boost/circular_buffer.hpp>
#include <iostream>
#include <functional>

struct LogHistory {
    using FormatFn = std::function<void(std::ostream&)>;
    boost::circular_buffer<FormatFn> last_10_logs_{10};

    template<class... T>
    void log(T&&... args) {
        last_10_logs_.push_back([=](std::ostream& s) { (s << ... << args); });
    }
};

int main() {
    LogHistory l;
    for(int i = 0; i < 20; ++i)
        l.log("hello ", i);

    for(auto const& f : l.last_10_logs_) {
        f(std::cout);
        std::cout << '\n';
    }
}

輸出:

hello 10
hello 11
hello 12
hello 13
hello 14
hello 15
hello 16
hello 17
hello 18
hello 19

如果您想向用戶顯示這些數據,遲早您仍然需要將其字符串化(大多數記錄器都以這種方式工作)。 但是,如果需要始終將其保留為二進制數據(假設您收集的數據稍后轉儲到文件中並可以按需解碼),那么您可以查看std::any ,它在運行時存儲類型數據,但是這樣修改將需要對您的代碼進行更大的更改。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM