0

I am using the spdlog library in a C++20 project using modules.

I've encapsulated spdlog inside my singleton logger class because I don't want spdlog exposed to clients in case I swap it out for a different log library someday. But now I want a macro that will call my singleton's "Trace()" method when not in the distribution configuration and be a no-op in Distribution. My macro is defined like this in Log.h:

#if defined(TS_DEBUG) || defined(TS_RELEASE)
    #define TS_LOG_TRACE(...) ::ThreeSpace::Logger::Trace(__VA_ARGS__)
#else
    #define TS_LOG_TRACE(...)
#endif

The macro then would forward the arguments to my public logger method. My method would simply pass those arguments on to spdlog's trace() method, which has the following signature:

template<typename... Args>
inline void trace(format_string_t<Args...> fmt, Args &&...args);

Clients would use it like this:

TS_LOG_TRACE("First value is {} and second value is {}", 42, 45);

I am having a heck of a time figuring out how to write my Logger singleton's trace() method.

I tried

template<typename... Args>
void Logger::Trace(std::format_string<Args...> fmt, Args &&...args)
{
    GetInstance()->trace(fmt, std::forward<Args>(args)...);
}

but it gives me the error

Error C2664: 'void spdlog::logger::trace<_Ty,_Ty>
fmt::v9::basic_format_string<char,int,int>,_Ty &&,_Ty &&)':
cannot convert argument 1 from 'std::basic_format_string<char,int,int>' to
'fmt::v9::basic_format_string<char,int,int>'
ThreeSpace  C:\Dev\ThreeSpace\Core\src\Logger.cppm  90  


I feel like I'm close but can't figure it out. What am I doing wrong?

Thanks for the help!

Edit: So I've discovered that if I define the templated method like this:

template<typename... Args>
    void Logger::Trace(std::format_string<Args...> fmt, Args &&...args)
    {
       std::string str = std::format(fmt, std::forward<Args>(args)...);
       std::cout << str;
    }

it works and prints the expected message to the console. But if I change the body of the method to this, it fails:

GetInstance()->trace(fmt, std::forward<Args>(args)...);

Due to @HolyBlackCat's suggestion in the comments below, I tried:

template<typename... Args>
    void Logger::Trace(fmt::format_string<Args...> fmt, Args &&...args)
    {
        Get()->trace(fmt, std::forward<Args>(args)...);
    }

I get this error:

2>C:\Dev\ThreeSpace\ThreeSpace\src\Main.cppm(21): fatal  error C1116: unrecoverable error importing module 'Logger'.  Specialization of 'std::unique_ptr<spdlog::formatter,std::default_delete<spdlog::formatter>>::unique_ptr' with arguments 'spdlog::pattern_formatter, std::default_delete<spdlog::pattern_formatter>, 0'
2>C:\Dev\ThreeSpace\ThreeSpace\src\Main.cppm(21,1): message : see reference to class template instantiation 'std::is_convertible<spdlog::pattern_formatter *,_Ty *>' being compiled
2>        with
2>        [
2>C:\Program Files\Microsoft Visual Studio\2022\Community\MSBuild\Microsoft\VC\v170\Microsoft.CppCommon.targets(544,5): error MSB6006: "CL.exe" exited with code 2.
2>            _Ty=spdlog::formatter
2>        ]
2>C:\Dev\ThreeSpace\ThreeSpace\src\Main.cppm(21): message : see reference to variable template 'const bool conjunction_v<std::negation<std::is_array<spdlog::pattern_formatter> >,std::is_convertible<spdlog::pattern_formatter *,spdlog::formatter *>,std::is_convertible<std::default_delete<spdlog::pattern_formatter>,std::default_delete<spdlog::formatter> > >' being compiled
Todd Burch
  • 200
  • 8

1 Answers1

0

I did get my original code to work with regular header files (traditional non-module-based C++) as @HolyBlackCat suggested in the original question, but I finally figured out the problem and got it working with modules:

In my Log.h, I had just the macro defined:

#pragma once

#if defined(TS_DEBUG) || defined(TS_RELEASE)
     #define TS_LOG_TRACE(...)   ::ThreeSpace::Logger::Trace(__VA_ARGS)
#else
    #define TS_LOG_TRACE(...)
#endif

My main module (Main.cppm) looked like this:

module;

#include "Log.h" //For access to the macro.

export module Main;

import Logger;

export int main()
{
     TS_LOG_TRACE("My favorite number is {}.", 42);
}

If you compile this, you get the cryptic error:

Error C1116: unrecoverable error importing module 'Logger'.  Specialization of
'std::unique_ptr<spdlog::formatter,std::default_delete<spdlog::formatter>>::unique_ptr'
with arguments 'spdlog::pattern_formatter, std::default_delete<spdlog::pattern_formatter>, 0'

ThreeSpace  C:\Dev\ThreeSpace\ThreeSpace\src\Main.cppm  20  

As it turns out, if I go back to the Log.h file and include the spdlog/pattern_formatter.h file in there, the error goes away:

#pragma once

#include "spdlog/pattern_formatter.h"         //No idea why this is needed, but it is!

.
.
.

This seems strange to me, since the usage of spdlog is entirely internal to the module (the whole reason I'm encapsulating it). My best guess as to why it's needed is because the variadic macro expands to a templated member function that takes fmt::format_string<T>, so that needs to be declared in the translation unit (via the Log.h file).

I'll need to find out what within the pattern_formatter.h file I can pull out to keep the preprocessing/compiler load to a minimum.

Todd Burch
  • 200
  • 8