如何将fmt::format_string<Args...>转为std::string_view?ISR编译错误求助
问题:ISR兼容的日志函数中fmt::format_string转std::string_view编译错误
我要实现一个能在ISR(中断服务程序)和任务上下文都能调用的日志函数。ISR里不能动态分配内存,所以没法调用fmt::format(),只能把fmt::format_string<Args...>转成std::string_view直接入队,但转换时出现编译错误:
(67): error C7595: 'fmt::v9::basic_format_string ::basic_format_string': call to immediate function is not a constant expression
最小复现代码
#include <fmt/format.h> #include <string> #include <string_view> #include <source_location> #include <concepts> #include <cassert> class DebugQueue { public: bool addToTaskQueue( std::string& strMessage, std::string& strCategeory, const std::source_location loc ) { return false; }; bool addToISRQueue( const std::string_view strFormatedDebugMessage, const std::string_view strCategory, const std::source_location loc) { return false; }; }; class Log { public: template <typename... Args> bool logAsync( fmt::format_string<Args...> fmt, const std::string_view strCategory, Args&&... args ); template <typename String_T> bool logAsync( String_T& strFormatedDebugMessage, String_T& strCategory, const std::source_location loc = std::source_location::current() ); static bool inHandlerMode(); private: DebugQueue m_queue; }; bool Log::inHandlerMode() { return false; } template<typename ...Args> bool Log::logAsync( fmt::format_string<Args...> fmt, const std::string_view strCategory, Args&&... args) { //if called from ISR we cannot call formatting functions since they will implicitly call new bool bRes = false; if (inHandlerMode()) { fmt::basic_string_view<char> asStringView = fmt; bRes = logAsync(std::string_view(asStringView.data()), strCategory); } else { std::string strFormatedMessage = fmt::format(fmt, std::forward<Args>(args)...); std::string strCat(strCategory); bRes = logAsync(strFormatedMessage, strCat); } } template<typename String_T> bool Log::logAsync( String_T& strFormatedDebugMessage, String_T& strCategory, const std::source_location loc) { bool bRes = false; if (inHandlerMode()) { //called from ISR, do not use any dynamic memory allocation //just add the unformated message to the queue bRes = m_queue.addToISRQueue(strFormatedDebugMessage, strCategory, loc); } else { //called from Task Context std::string strMsg; std::string strCat; if constexpr (std::same_as<std::string, String_T>) { std::swap(strMsg, strFormatedDebugMessage); std::swap(strCategory, strCat); } else { strMsg = std::string(strFormatedDebugMessage.data()); strCategory = std::string(strCat.data()); } bRes = m_queue.addToTaskQueue(strFormatedDebugMessage, strCategory, loc); } } static Log g_log; int main() { fmt::print("The answer is {}.", 42); g_log.logAsync("Info {}", "fooCat",1); g_log.logAsync("Info", "fooCat"); }
问题原因
fmt::format_string是立即数类型(immediate type),它的构造和转换操作要求在常量表达式上下文中完成,但代码中通过inHandlerMode()做了运行时判断,导致转换操作脱离了常量上下文,触发编译错误。
解决方法
关键修改点
- 直接访问
fmt::format_string的底层存储:绕过立即数转换限制,调用fmt.data()和fmt.size()获取原始字符串视图,这两个成员是constexpr属性,可安全在运行时分支中使用; - 修正重载函数的参数类型:将
String_T&改为const String_T&,支持右值参数(比如临时生成的string_view); - 补充返回值与修正逻辑错误:原代码漏写函数返回值,且任务上下文分支的字符串转换逻辑存在反向赋值错误,一并修复。
修改后的完整代码
#include <fmt/format.h> #include <string> #include <string_view> #include <source_location> #include <concepts> #include <cassert> class DebugQueue { public: bool addToTaskQueue( std::string& strMessage, std::string& strCategeory, const std::source_location loc ) { return false; }; bool addToISRQueue( const std::string_view strFormatedDebugMessage, const std::string_view strCategory, const std::source_location loc) { return false; }; }; class Log { public: template <typename... Args> bool logAsync( fmt::format_string<Args...> fmt, const std::string_view strCategory, Args&&... args ); template <typename String_T> bool logAsync( const String_T& strFormatedDebugMessage, const String_T& strCategory, const std::source_location loc = std::source_location::current() ); static bool inHandlerMode(); private: DebugQueue m_queue; }; bool Log::inHandlerMode() { return false; } template<typename ...Args> bool Log::logAsync( fmt::format_string<Args...> fmt, const std::string_view strCategory, Args&&... args) { bool bRes = false; if (inHandlerMode()) { // 直接获取format_string的底层字符串视图 std::string_view fmt_view(fmt.data(), fmt.size()); bRes = logAsync(fmt_view, strCategory); } else { std::string strFormatedMessage = fmt::format(fmt, std::forward<Args>(args)...); std::string strCat(strCategory); bRes = logAsync(strFormatedMessage, strCat); } return bRes; } template<typename String_T> bool Log::logAsync( const String_T& strFormatedDebugMessage, const String_T& strCategory, const std::source_location loc) { bool bRes = false; if (inHandlerMode()) { bRes = m_queue.addToISRQueue(strFormatedDebugMessage, strCategory, loc); } else { std::string strMsg; std::string strCat; if constexpr (std::same_as<std::string, String_T>) { strMsg = strFormatedDebugMessage; strCat = strCategory; } else { strMsg = std::string(strFormatedDebugMessage.data(), strFormatedDebugMessage.size()); strCat = std::string(strCategory.data(), strCategory.size()); } bRes = m_queue.addToTaskQueue(strMsg, strCat, loc); } return bRes; } static Log g_log; int main() { fmt::print("The answer is {}.", 42); g_log.logAsync("Info {}", "fooCat",1); g_log.logAsync("Info", "fooCat"); }
内容的提问来源于stack exchange,提问作者user8694474
相关产品推荐
相关产品推荐

