Search code examples
c++fmtif-constexpr

c++ convert fmt::format_string<Args...>to std::string_view


I'm currently struggling to convert fmt::format_string<Args...>to a std::string_view.

The idea: I would like to create a fucntion with can be called from an ISR and task context. However in an ISR no dynamic memory allocation is allowed. That's why i cannot call fmt::fomat() in this case. However I'm getting a strange compiler error

<source>(67): error C7595: 'fmt::v9::basic_format_string<char>::basic_format_string': call to immediate function is not a constant expression

Here is a minimal working example

#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");
}

https://godbolt.org/z/d7jeTjT6f

thx for your help guys :)


Solution

  • as suggested by @user17732522 in the comments the answer is changing String_T& to String_T and renaming

    template <typename String_T>
        bool logAsync(
          String_T& strFormatedDebugMessage,
          String_T& strCategory,
          const std::source_location loc = std::source_location::current()
        );
    

    to

    template <typename String_T>
        bool _logAsync(
          String_T& strFormatedDebugMessage,
          String_T& strCategory,
          const std::source_location loc = std::source_location::current()
        );
    

    did solve my problem. Thx guys for your help:)