隐藏库

时间:2017-07-21 13:35:53

标签: c++ pimpl-idiom template-function

首先,大局。我有一个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

HIDE LOGGER ATTEMPT

我试图使用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类。

1 个答案:

答案 0 :(得分:1)

简单的答案是 - 你做不到。问题是接口方法被实例化为具有每个不同参数集的“不同函数”,并且每次创建Logger函数模板的不同实例化。为了做到这一点,它需要可以访问Logger方法的整个定义才能生成新的实例化。

如果你隐藏它,你确实会得到所有丢失的实例化的未解析的外部因素,因为它们不是在构建LoggerInterface.cpp时创建的(因为当时不知道将需要所有实例化)。

如果只有一些有限的可能实例化,您可以显式实例化所有需要的版本,它们可以从库中导出/通过PIMPL使用。但是,由于这不是这种情况(log()方法的参数的类型和计数可能非常随意),在这种情况下,此解决方案不实用。