You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何将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()做了运行时判断,导致转换操作脱离了常量上下文,触发编译错误。

解决方法

关键修改点

  1. 直接访问fmt::format_string的底层存储:绕过立即数转换限制,调用fmt.data()和fmt.size()获取原始字符串视图,这两个成员是constexpr属性,可安全在运行时分支中使用;
  2. 修正重载函数的参数类型:将String_T&改为const String_T&,支持右值参数(比如临时生成的string_view);
  3. 补充返回值与修正逻辑错误:原代码漏写函数返回值,且任务上下文分支的字符串转换逻辑存在反向赋值错误,一并修复。

修改后的完整代码

#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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.11 22:40:31