Summary
I'm writing a library and a client application. In the library I'm trying to write a wrapper around another statically linked third-party library (specifically, spdlog) and am trying to use the pImpl idiom to completely hide it from the client application. The problem is that the third-party library uses variadic template functions, and so I need to in my library too.
Background
My first attempt at a wrapper was pretty thin and straight forward, but then I was getting "No such file or directory" errors in my client application because the third-party header was being included in my library's header.
I next tried to create a pImpl class and got that to compile, but again in the client I was getting "undefined reference" linker errors.
Pulling the source code for the implementation into the header for my wrapper puts me right back at the initial "No such file" problem. After researching this I'm starting to think making a wrapper around variadic templates isn't possible, but I'm not sure. This is the first time I've ever tried to make a variadic function/template.
Example Code
Here's how my project currently stands:
Almost all namespaces, function names, headers, etc. have all been edited (or removed) for brevity and clarity.
Client Application - 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;
}
My Library - 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. */
My Library - 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...);
}
My Library - 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.
}
}
Expected Results
As mentioned above I just want clients of my library to be able to include headers like <logger.h>
and not need to configure their projects to find and handle all of my library's dependencies as well, but since I'm currently using a third-party tool that uses variadic templates I'm not seeing any way that I can hide that from my clients given the, um... "not a real function" nature of templates.
Logger::info
in a.cpp
file. Are you aware that all template definitions needs to be available at compile time? That's why you normally put them in the header. That includes the ones in your third party dependencies. – Saran