How to Bridge Incompatible C++ Logging Interfaces with Adapter Patterns
A C++ developer faces a mismatched logging API between an internal ILogger interface and a third‑party FastLogger library, and the article walks through class‑adapter inheritance, its limitations, and a more flexible object‑adapter composition solution that enables runtime switching across multiple logger implementations.
Problem statement
A core C++ system defines a unified logging interface ILogger with two pure‑virtual methods that accept std::string:
class ILogger {
public:
virtual void log(const std::string& message) = 0;
virtual void error(const std::string& message) = 0;
};All modules depend on ILogger*. A third‑party library provides a faster logger with a different API:
class FastLogger {
public:
void writeLog(const char* msg, int level);
};The two interfaces are incompatible because:
Parameter types differ ( std::string vs const char*).
Method names and semantics differ ( log / error vs a single writeLog with a numeric level).
The system expects an ILogger* pointer, while the library supplies an unrelated class.
Changing every occurrence of ILogger would require massive code modifications and would lock the system to a specific third‑party implementation.
Class adapter – inheritance based solution
One way to bridge the gap is to create a class that inherits both ILogger and FastLogger and translates the calls:
class LoggerAdapter : public ILogger, public FastLogger {
public:
void log(const std::string& message) override {
writeLog(message.c_str(), 0); // 0 = normal log
}
void error(const std::string& message) override {
writeLog(message.c_str(), 1); // 1 = error level
}
};
// Registration example
LoggerRegistry registry;
LoggerAdapter* adapter = new LoggerAdapter();
registry.registerLogger(adapter);
registry.broadcast("Hello World");This adapter can be registered where an ILogger* is required, while internally it uses the third‑party FastLogger implementation.
Limitation : The adapter is bound at compile time to the concrete FastLogger class. If the vendor releases new variants (e.g., HighPerfLogger, LowPowerLogger), a separate adapter class must be written for each variant.
Object adapter – composition based solution
To avoid a proliferation of adapter classes, use composition: the adapter holds a pointer to any FastLogger derived object and forwards calls.
class LoggerAdapter : public ILogger {
private:
FastLogger* adaptee_; // composition
public:
explicit LoggerAdapter(FastLogger* logger) : adaptee_(logger) {}
void log(const std::string& message) override {
adaptee_->writeLog(message.c_str(), 0);
}
void error(const std::string& message) override {
adaptee_->writeLog(message.c_str(), 1);
}
void setAdaptee(FastLogger* logger) { adaptee_ = logger; }
};
// Adapting different concrete loggers
LoggerAdapter* a1 = new LoggerAdapter(new FastLogger());
LoggerAdapter* a2 = new LoggerAdapter(new HighPerfLogger());
LoggerAdapter* a3 = new LoggerAdapter(new LowPowerLogger());
// Runtime switching example
FastLogger* current = new HighPerfLogger();
LoggerAdapter* adapter = new LoggerAdapter(current);
adapter->setAdaptee(new LowPowerLogger()); // switch implementation at runtimeBecause adaptee_ is a base‑class pointer, a single LoggerAdapter can adapt the entire inheritance hierarchy, eliminating the need for multiple adapter classes and allowing the implementation to be swapped without recompilation.
Key differences between class and object adapters
Class adapter (inheritance) : The adapter inherits the concrete third‑party class, fixing the implementation at compile time.
Object adapter (composition) : The adapter holds a pointer/reference to the abstract third‑party base class, enabling runtime selection of any derived implementation.
Practical insight
When integrating incompatible interfaces, prefer an object adapter that uses composition. It provides:
Flexibility to work with any current or future subclass of the third‑party library.
Ability to change the logging implementation at runtime (e.g., switch between HighPerfLogger on servers and LowPowerLogger on mobile devices).
Minimal impact on existing code: the system continues to depend only on ILogger.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Java Tech Enthusiast
Sharing computer programming language knowledge, focusing on Java fundamentals, data structures, related tools, Spring Cloud, IntelliJ IDEA... Book giveaways, red‑packet rewards and other perks await!
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
