CONTEXT
我有一个由多个软件使用的库。一些是基本命令行,一些是Qt UI。
我想在这个库中实现一个独特的日志类,所以每个软件都可以使用它。但是,如果我们处于Qt环境中,我希望将qDebug()
函数与特定QtMessageHandler
一起使用。
目标是能够在此库中记录错误,并且日志将以不同方式打印,具体取决于库是否在UI环境中使用(库不依赖于Qt)。
我想要什么
在全球范围内,我想要这样的事情:
class Logger
{
public:
template <class T>
void log(const T& tolog) { handler.log(tolog); }
void setHandler(HANDLER???& h) { handler = h; }
const HANDLER???& getHandler() const { return handler; }
private:
HANDLER??? handler;
}
对于命令行软件,使用处理程序非常简单,如:
class CLHandler
{
public:
template <class T>
void log(const T& tolog) { out << tolog << std::endl; }
private:
std::ostream out;
}
对于UI,我想使用qDebug()
,因此我可以设置自定义QtMessageHandler
以在UI中打印错误,并将其记录在文件中:
class UIHandler
{
public:
template <class T>
void log(const T& tolog) { qDebug() << tolog; }
}
问题
正如您所看到的,问题出现在类Logger
中:处理程序将是什么类型?
由于虚拟模板功能,我无法真正创建界面:
class IHandler
{
public:
virtual ~IHandler() = default;
template <class T>
virtual void log(const T& tolog) = 0; // ERROR templated virtual function!
}
需要帮助
我希望模仿IHandler::tolog
函数,因为我想对operator<<
和ostream
使用qDebug()
的强大功能。而且我不想自己重新实现所有重载(ostream的长列表,qDebug甚至更长!)。
我想实现它,无论怎样(lambda函数与auto
?)...欢迎任何建议(我可能在这里做错了吗?)。
谢谢:)
答案 0 :(得分:2)
显然不可能有模板化的虚函数,但你可以使用类型擦除来“擦除”具体类型,所以你不再需要模板了。
基本上,您创建了一个接口ILoggableValue
,它知道如何使用QDebug
和std::ostream
流记录您的值,并使用模板生成不同类型的具体实现:
class ILoggableValue {
public:
virtual ~ILoggableValue() = default;
virtual void log(QDebug &os) const = 0;
virtual void log(std::ostream &os) const = 0;
};
template <typename T>
class LoggableValue : public ILoggableValue {
public:
LoggableValue(const T &value) : value{value} {}
void log(QDebug &os) const override {
// implementation of log for QDebug goes here
os << value;
}
void log (std::ostream &os) const override {
// implementation of log for std::ostream goes here
os << value << std::endl;
}
private:
const T &value;
};
然后,您按照建议的方式创建IHandler
,但现在您可以使用ILoggableValue
删除模板:
class IHandler {
public:
virtual ~IHandler() = default;
virtual void log(const ILoggableValue &tolog) const = 0;
};
class CLHandler : public IHandler {
public:
explicit CLHandler(std::ostream &out) : out{out} {}
void log(const ILoggableValue &tolog) const override {
tolog.log(out);
}
private:
std::ostream &out;
};
class UIHandler : public IHandler {
public:
void log(const ILoggableValue &tolog) const override {
tolog.log(qDebug());
}
};
最后,您在IHandler
中使用Logger
:
class Logger {
public:
Logger(std::unique_ptr<IHandler> h) : handler(std::move(h)) {}
template <class T>
void log(const T& tolog) { handler->log(LoggableValue<T>(tolog)); }
void setHandler(std::unique_ptr<IHandler> &h) { handler = std::move(h); }
const IHandler &getHandler() const { return *handler; }
private:
std::unique_ptr<IHandler> handler;
};
Here是一个实例。