摘要
我正在编写一个库和客户端应用程序。在库中,我试图在另一个静态链接的第三方库(具体来说是 spdlog)周围编写一个包装器,并尝试使用 pImpl 习惯用法完全隐藏它,以避免对客户端应用程序产生影响。问题是第三方库使用可变参数模板函数,因此我也需要在我的库中使用。
背景
我第一次尝试编写一个包装器时非常简单直接,但是在客户端应用程序中出现了“没有这样的文件或目录”的错误,因为第三方头文件被包含在我的库头文件中。
然后我尝试创建一个 pImpl 类,并将其编译成功,但是在客户端中仍然出现“未定义引用”的链接器错误。
将实现的源代码拉入我的包装器头文件中会让我回到最初的“没有这样的文件”问题。经过研究,我开始认为在可变参数模板周围创建包装器是不可能的,但我不确定。这是我第一次尝试制作可变函数/模板。
示例代码
这是我的项目当前的状态:
为了简洁和清晰起见,几乎所有的命名空间、函数名、头文件等都已经被编辑(或删除)。
客户端应用程序 - sandbox.cpp
#include "sandbox.h"
#include <logger.h> // <-- This is all I want clients to see.
int Sandbox::run() {
LOG_INFO("Hello World!"); // My library is providing this.
LOG_INFO("Hello {}", "indeed!"); // And, this variable input function.
return 0;
}
我的库 - logger.h
class LoggerImp; // Forward declaration of implementation.
class LIB_EXPORT Logger {
public:
/* Constructors, destructor, etc. */
template <typename... Args>
void info(const char * fmt, Args &... args);
void info(const char * msg) { this->info("{}", msg); }
/* Other logging functions: trace, error, etc. */
private:
LoggerImp * _imp;
};
static Logger coreLogger("Core");
static Logger clientLogger("App");
#define LOG_INFO(args...) clientLogger.info(args)
/* Other such convenience definitions. */
我的库 - logger.cpp
#include "logger.h"
#include "loggerimp.h"
Logger::Logger(std::string name) { _imp = new LoggerImp(name, this); }
Logger::~Logger() { delete _imp; }
template <typename... Args>
void Logger::info(const char * fmt, Args &... args) {
_imp->info(fmt, args...);
}
我的库 - loggerimp.h
#include "logger.h"
#include <spdlog/spdlog.h>
#include <spdlog/sinks/stdout_color_sinks.h>
class LoggerImp {
public:
explicit LoggerImp(string name, Logger * pubInterface) :
_pubInterface(pubInterface) { // Back pointer.
_sink = make_shared<spdlog::sinks::stdout_color_sink_mt>();
_logger = make_shared<spdlog::logger>(name, _sink);
spdlog::initialize_logger(_logger);
// The above three lines create the actual logging object
// that my library is wrapping and hiding from its clients.
}
template <typename... Args>
inline void info(const char * fmt, const Args &... args) {
_logger->info(fmt, args...); // Third-party logging function.
}
}
预期结果
如上所述,我只希望我的库的客户端能够包含像<logger.h>
这样的头文件,而不需要配置其项目以找到和处理我库的所有依赖项,但由于我目前正在使用一个使用可变参数模板的第三方工具,我没有看到任何方法可以隐藏它,因为模板的 "非真实函数" 性质。
.cpp
文件中定义了Logger::info
,你知道所有模板定义都需要在编译时可用吗?这就是为什么通常将它们放在头文件中的原因,包括第三方依赖项中的模板。 - superformat
使用类似的语法甚至是相同的语法,您可以通过可变参数和格式转换为单个参数(字符串),然后将其转发到您的实现。 - JVApen