[英]Is there a better alternative to std::unordered_map<std::string, std::any> to have a map of strings to any type of value?
我正在編寫輸出到控制台的日志 function。 我最終要使用的語法如下所示:
Log(Info,
"Message",
{ {"Key1", value1},
{"Key2", value2} })
其中value1
和value2
可能不是同一類型(可能是一個 std::string 的名稱和一個 int 他們的年齡)。 有沒有更好的方法使用std::unordered_map<std::string, std::any>
存儲鍵值對並使用std::any_cast
在輸出到控制台之前推斷類型?
如果可能的類型數量有限,請使用std::variant
而不是std::any
,例如
std::unordered_map<std::string, std::variant<int, std::string, etc>>
您可以通過std::variant::index()
或std::holds_alternative()
查詢std::variant
當前持有的類型,然后通過std::get()
檢索值(使用index()
例如,使用get()
在switch()
塊中會很有用)。 或者,如果它與給定類型匹配,您可以使用std::get_if()
來獲取指向數據的指針。
或者,您可以使用std::visit()
,這在日志記錄用例中很有用,因為您可以根據需要提供單獨的訪問者來記錄每種可能的類型。
是的,有更好的方法:
#include <iostream>
#include <string_view>
#include <sstream>
struct LogPair{
template<typename T>
LogPair(std::string_view key, T&& value){
std::stringstream buffer;
//Feel free to change
buffer<<key <<": "<<std::forward<T>(value);
msg = buffer.str();
}
std::string msg;
};
enum class LogLevel{
Info = 0,
};
void log(LogLevel level, std::string_view msg, std::initializer_list<LogPair> args){
(void)level;//TODO
std::cout<<msg<<'\n';
for(const LogPair& s: args)
std::cout<<'\t'<<s.msg<<'\n';
}
struct Test{//Complex type with custom print
float val;
friend std::ostream& operator<<(std::ostream& os, const Test& t){
os<<t.val;
return os;
}
};
用法:
int main(){
int value1 = 10;
Test value2{1.4};
log(LogLevel::Info,"Message", { {"Key1", value1},{"Key2", value2} });
}
Output:
Message
Key1: 10
Key2: 1.4
完美的解決方案類似於@Miles Budnek 發布的解決方案:
template <typename... Params>
void Log(LogLevel level, std::string_view message, const Params&... params);
或成對:
template <typename... Params>
void Log(LogLevel level, std::string_view message, const std::pair<std::string_view,Params>&... params)
但是這兩種情況都不會編譯:
Log(level, "Msg", {"Key1",value1},{"Key2", value2})
這是必需的:(@Miles 使用了一個元組)
Log(level, "Msg", std::pair{"Key1",10},std::pair{"Key2", 1.2})
原因是即使我們可以看到Params
應該被推導為int,double
,編譯器卻沒有,因為大括號語法是不可推導的。 推導指南在這里沒有幫助(即使我們將其重新制作為 Log 構造函數),因為實際上沒有辦法匹配大括號語法,因為它甚至沒有類型。 我不相信有可變參數模板的解決方案。
但是,您提供的語法可以導致解決方案。 唯一的希望是將大括號初始值設定項語法轉換為std::initializer_list<T>
,但是 T 是什么? 它應該看起來像std::pair<std::string_view,?>
但每一對都是不同的。 在這里,您可以使用std::any
並且它會起作用,有一種更簡單的方法。
使用模板構造函數創建非模板class LogPair
,該構造函數可以接受匹配任何對的大括號初始值設定項語法!
template<typename T> LogPair(std::string_view key, T&& value);
差不多了,現在 log 可以接受std::initializer_list<LogPair>
因為它正確匹配語法並且不正確的使用會導致編譯時錯誤。
現在,很誘人的是在構造函數中進行打印,但未指定這些變量的創建順序,並且它也會在消息打印之前。 因此,解決方案是將其保存到稍后打印的字符串中。
請注意,如果不重用或重新實現std::any
,就無法將值保存在LogPair
中。 但這不應該是必需的。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.