首先,大局。我有一个Logger类。我为该类创建了一个简化的接口,并为该接口创建了一个库。我想使用pimpl隐藏Logger类实现,因此用户不需要Logger的头文件。我的模板功能很糟糕......
Logger标头定义如下
/* Logger.h */
class Logger
{
public:
virtual ~Logger(){};
public:
template <typename... Args> void log(const char* fmt, const Args&... args)
{
printf(fmt, &args...);
}
};
std::shared_ptr<Logger> create_logger()
{
return std::shared_ptr<Logger>(new Logger());
}
我已经创建了这样的界面
/* LoggerInterface.h */
#include "Logger.h"
class LoggerInterface
{
public:
LoggerInterface();
public:
template <typename... Args> void log(const char* fmt, Args&&... args)
{
logger->log(fmt, std::forward<Args>(args)...);
}
private:
std::shared_ptr<Logger> logger;
};
/* LoggerInterface.cpp */
#include "LoggerInterface.h"
LoggerInterface::LoggerInterface()
{
logger = create_logger();
}
我已经生成了库,这里是一个使用它的main.cpp示例
/* main.cpp */
#include <LoggerInterface.h>
int main()
{
LoggerInterface loggerIntreface;
loggerIntreface.log("Welcome %s\n", "logger");
return 0;
}
一切正常,但主要包括LoggerInterface.h
,因此隐含地包含Logger.h
。我想在用户代码方面摆脱Logger.h
。
我试图使用pimpl习语,但模板功能令我头疼。我已经阅读了几篇文档并做了很多测试,但到目前为止还没有运气。这是“几乎在那里”版本的代码。
/* LoggerInterface.h */
class LoggerInterface
{
private:
class LoggerImpl;
public:
LoggerInterface();
public:
template <typename... Args> void log(const char* fmt, Args&&... args);
private:
std::shared_ptr<LoggerImpl> logger;
};
/* LoggerInterfacePrivate.h */
#include "Logger.h"
#include "LoggerInterface.h"
class LoggerImpl : public Logger
{}
template <typename... Args> inline void LoggerInterface::log(const char* fmt, Args&&... args)
{
logger->log(fmt, std::forward<Args>(args)...);
}
/* LoggerInterface.cpp */
#include "LoggerInterfacePrivate.h>
LoggerInterface::LoggerInterface()
{
logger = std::dynamic_pointer_cast<Logger>(create_logger());
}
主要方面没什么新东西
/* main.cpp */
#include <LoggerInterface.h>
int main()
{
LoggerInterface loggerIntreface;
loggerIntreface.log("Welcome %s\n", "logger");
return 0;
}
主要包括LoggerInterface.h
,但由于pimpl而不需要Logger.h
。编译好了,但是我很难得到模板log()
函数的未解析的外部符号错误。
有关如何摆脱错误的任何想法?我的方法是好方法,还是更好地遵循不同的实现来实现我的目标(创建一个用户可以在没有基本Logger类头的情况下使用的库接口)?
重要说明:我无法编辑Logger类。
答案 0 :(得分:1)
简单的答案是 - 你做不到。问题是接口方法被实例化为具有每个不同参数集的“不同函数”,并且每次创建Logger函数模板的不同实例化。为了做到这一点,它需要可以访问Logger方法的整个定义才能生成新的实例化。
如果你隐藏它,你确实会得到所有丢失的实例化的未解析的外部因素,因为它们不是在构建LoggerInterface.cpp时创建的(因为当时不知道将需要所有实例化)。
如果只有一些有限的可能实例化,您可以显式实例化所有需要的版本,它们可以从库中导出/通过PIMPL使用。但是,由于这不是这种情况(log()方法的参数的类型和计数可能非常随意),在这种情况下,此解决方案不实用。