[英]{fmt}: always compile-time check format string in function
我正在嘗試創建一個自定義錯誤 class,其構造函數通過將 arguments 傳遞給fmt::format()
來創建錯誤消息。 我希望它總是在編譯時檢查 arguments 的格式字符串,而不必每次拋出時都顯式使用FMT_STRING()
。 就像是:
class Err : public std::exception
{
private:
std::string m_text;
public:
template <typename S, typename... Args>
Err(const S& format, Args&&... args) {
m_text = fmt::format(FMT_STRING(format), args...);
}
virtual const char* what() const noexcept {return m_text.c_str();}
};
// ------------------------
throw Err("Error {:d}", 10); // works
throw Err("Error {:d}", "abc"); // cause Compile-time error
使用前面的代碼,我在 FMT_STRING() 宏上遇到錯誤:
error C2326: 'Err::{ctor}::<lambda_1>::()::FMT_COMPILE_STRING::operator fmt::v7::basic_string_view<char>(void) const': function cannot access 'format'
message : see reference to function template instantiation 'Err::Err<char[11],int>(const S (&),int &&)' being compiled with [ S=char [11] ]
我對模板編程的經驗很少。 如何讓它始終在編譯時檢查格式字符串,而無需每次都明確使用FMT_STRING()
?
您只能使用此 github 問題中說明的方法在 C++20 中執行此類編譯時檢查:
#include <fmt/format.h>
template <typename... Args>
struct format_str {
fmt::string_view str;
template <size_t N>
consteval format_str(const char (&s)[N]) : str(s) {
using checker = fmt::detail::format_string_checker<
char, fmt::detail::error_handler, Args...>;
fmt::detail::parse_format_string<true>(
fmt::string_view(s, N), checker(s, {}));
}
};
template <class... Args>
std::string format(
format_str<std::type_identity_t<Args>...> fmt,
const Args&... args) {
return fmt::format(fmt.str, args...);
}
int main() {
auto s = format("{:d}", 42);
fmt::print("{}\n", s);
}
Godbolt 演示: https://godbolt.org/z/qrh3ee
我今天遇到了同樣的問題,但我認為 {fmt} 庫從那以后有所改進,所以這是我的解決方案。
這里的想法是在異常構造函數中進行與 fmt::format 調用完全相同的調用:使用 C++20 consteval 構建 fmt::format_string object 將在編譯時解析和檢查格式字符串。 然后,將格式字符串和可變參數傳遞給 vformat,這才是真正的工作。
#include <fmt/format.h>
#include <exception>
#include <iostream>
class Err : public std::exception
{
private:
std::string m_text;
public:
template <typename... Args>
Err(fmt::format_string<Args...> fmt, Args&&... args) {
m_text = fmt::vformat(fmt, fmt::make_format_args(args...));
}
virtual const char* what() const noexcept {return m_text.c_str();}
};
int main() {
try {
throw Err("Error {:d}", 10); // Works
throw Err("Error {:d}", "abc"); // Compile error
}
catch(const std::exception& e){
std::cout << e.what() << std::endl;
}
}
在我的版本中,我最終為構造函數提供了一個覆蓋,該構造函數采用單個字符串並完全跳過格式化,並將格式化構造函數更改為:
Err(fmt::format_string<T, Args...> fmt, T&& p1, Args&&... args) {
m_text = fmt::vformat(fmt, fmt::make_format_args(p1, args...));
}
這是為了確保在只有一個參數時始終選擇另一個重載。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.