1

I have a project where I use the spdlog library. My header file looks like this:

#pragma once

#include <memory>
#include <string>
#include <spdlog/spdlog.h>

namespace vtek
{
    void initialize_logging(const InitInfo* info);
    void terminate_logging();
    void disable_logging();

    void flush_vtek_logger();
    void flush_client_logger();

    class LogContainer
    {
    public:
        static inline std::shared_ptr<spdlog::logger> sVtekLogger = nullptr;
        static inline std::shared_ptr<spdlog::logger> sClientLogger = nullptr;
    };
}

template<typename... Args>
inline void vtek_log_error(const char* message, const Args &... args)
{
    vtek::LogContainer::sVtekLogger->error(message, args...);
}

This code is working fine. I can include the header in any compilation unit and write vtek_log_error("Error, wrong parameter: {}", myParam);, and the templates in the logging header will figure out the rest.

Then I update the project (CMake settings) from C++17 to C++20, and this no longer works, and I don't really understand the compiler's message:

in ‘constexpr’ expansion of ‘fmt::v9::basic_format_string<char, const std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >&, const long unsigned int&>(message)’
/<path>/vtek_logging.h:97:47: error: ‘message’ is not a constant expression
   97 |         vtek::LogContainer::sVtekLogger->error(message, args...);

Why is message no longer a constant expression? It looks pretty "constant" to me, ie. vtek_log_error("A constant message of type const char*");

Is this a known problem? Or do I need to provide more information?

alexpanter
  • 1,222
  • 10
  • 25
  • What's the function signature for `error()`? – tadman Apr 14 '23 at 17:52
  • 3
    Somewhere in `error` you are using `fmt::format`. This is intentionally only usable with compile-time constants (or with an earlier constructed `fmt::format_string`) as arguments in order to allow for compile-time type checking of the format string. Before C++20 this feature enforcing the compile-time requirement was however impossible to implement, so that's probably why it doesn't complain with C++17. If `spdlog` is calling `fmt::format`, then that's a bug on their side. – user17732522 Apr 14 '23 at 17:56
  • 1
    see https://stackoverflow.com/a/69647103/5494370 – Alan Birtles Apr 14 '23 at 17:58
  • 1
    Addendum: Maybe `spdlog` also correctly using the method used in the link above, in which case it also expects the argument to `error` to be a compile-time constant. Then you'll have to see the documentation for an explanation of how to use the method with a runtime argument or prepare the format_string earlier. – user17732522 Apr 14 '23 at 18:03
  • @user17732522 Would you mind elaborating that a bit for me? I don't understand why `const char*` is not a compile-time constant. I have tried `const std::string_view` instead, which also does not work.. :/ – alexpanter Apr 14 '23 at 18:07
  • 2
    @alexpanter `message` inside `vtek_log_error` is not a compile-time constant because its value depends on the argument with which the function is called. In order to get a compile-time constant into a function you would need to pass the string as part of the _type_ of the parameter, i.e. as a template argument (but you can't pass either pointers/references to string literals or `string_view` or `string` as template arguments by-value). However, this issue should really be addressed by the API of spdlog. It should document if `error` requires a compile-time constant and provide alternatives. – user17732522 Apr 14 '23 at 18:11
  • 3
    It seems to provide the `SPDLOG_FMT_RUNTIME` macro in order to allow runtime format strings (but that will of course disable the compile-time format string verification and probably use runtime exceptions instead), see https://github.com/gabime/spdlog/search?q=SPDLOG_FMT_RUNTIME. – user17732522 Apr 14 '23 at 18:18

0 Answers0