c++ 在Variadic宏中 Package 对Spdlog的调用

wb1gzix0  于 2023-08-09  发布在  其他
关注(0)|答案(1)|浏览(112)

我在一个使用模块的C++20项目中使用spdlog库。
我将spdlog封装在我的单例日志记录器类中,因为我不希望spdlog暴露给客户端,以防有一天我将它换成不同的日志库。但是现在我想要一个宏,当不在分发配置中时,它将调用我的单例的“Trace()”方法,并且在分发中是一个空操作。我的宏在Log.h中是这样定义的:

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

字符串
然后,宏会将参数转发给我的公共记录器方法。我的方法只是将这些参数传递给spdlog的trace()方法,该方法具有以下签名:

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


客户会这样使用它:

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


我花了很长时间才弄清楚如何编写Logger单例的trace()方法。
我试过了

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


但它给了我错误

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


我觉得我很接近了,但我想不出来。我做错了什么?
谢谢你的帮助!
Edit:所以我发现如果我像这样定义模板化方法:

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;
    }


它工作并将预期的消息打印到控制台。但是如果我将方法的主体改为这样,它就会失败:

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


由于@HolyBlackCat在下面评论中的建议,我尝试了:

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


我得到这个错误:

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

dgenwo3n

dgenwo3n1#

正如@HolyBlackCat在最初的问题中所建议的那样,我确实让我的原始代码与常规头文件(传统的非基于模块的C++)一起工作,但我最终找到了问题所在,并让它与模块一起工作:
在我的Log.h中,我刚刚定义了宏:

#pragma once

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

字符串
我的主模块(Main.cppm)看起来像这样:

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


如果你编译它,你会得到一个神秘的错误:

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


事实证明,如果我回到Log. h文件并在其中包含spdlog/pattern_formatter. h文件,错误就会消失:

#pragma once

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

.
.
.


这对我来说似乎很奇怪,因为spdlog的使用完全是模块内部的(我封装它的全部原因)。对于为什么需要它,我最好的猜测是因为可变参数宏扩展为一个接受fmt::format_string<T>的模板化成员函数,因此需要在转换单元中声明(通过Log. h文件)。
我需要找出在pattern_formatter.h文件中可以提取哪些内容,以使预处理/编译器负载保持最小。

相关问题