Резюме
Пишу библиотеку и клиентское приложение. В библиотеке я пытаюсь написать оболочку для другой статически связанной сторонней библиотеки (в частности, 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>
, и им не нужно было настраивать свои проекты для поиска и обработки всех зависимостей моей библиотеки, но поскольку в настоящее время я использую сторонний инструмент, который использует вариативные шаблоны. Я не вижу способа скрыть это от своих клиентов, учитывая, гм... нереальную функциональную природу шаблонов.
Logger::info
в файле.cpp
. Знаете ли вы, что все определения шаблонов должны быть доступны во время компиляции? Вот почему вы обычно помещаете их в заголовок. Это включает в себя те, которые находятся в ваших сторонних зависимостях. - person super   schedule 09.06.2019