[英]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.