简体   繁体   English

多态记录器(虚拟模板函数?)

[英]Polymorphic logger (virtual templated functions?)

CONTEXT CONTEXT

I have a library used by several software. 我有一个由几个软件使用的库。 Some are basic command line and some have a Qt UI. 一些是基本命令行,一些是Qt UI。

I want to implement a unique log class inside this library, so every software can use it. 我想在这个库中实现一个独特的日志类,所以每个软件都可以使用它。 However, if we are in a Qt environment, I'd like to use the qDebug() function with a specific QtMessageHandler . 但是,如果我们处于Qt环境中,我想将qDebug()函数与特定的QtMessageHandler The goal is to be able to log errors inside this library, and the logs will be printed differently, depending on if the library is used in a UI environment or not (the library has no dependencies to Qt). 目标是能够记录此库中的错误,并且日志将以不同方式打印,具体取决于库是否在UI环境中使用(库与Qt无依赖关系)。


WHAT I WANT 我想要的是

Globally, I want something like this: 在全球范围内,我想要这样的事情:

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;
}

With the handler that will be, for command line software, something very simple like: 使用处理程序,对于命令行软件,非常简单,如:

class CLHandler
{
public:
    template <class T>
    void log(const T& tolog) { out << tolog << std::endl; }

private:
    std::ostream out;
}

and for UI, I want to use qDebug() so I can setup a custom QtMessageHandler to print the error in the UI, and log it in a file: 对于UI,我想使用qDebug()所以我可以设置一个自定义QtMessageHandler来在UI中打印错误,并将其记录在一个文件中:

class UIHandler
{
public:
    template <class T>
    void log(const T& tolog) { qDebug() << tolog; }
}

PROBLEM 问题

As you can see, the problem is in the class Logger : what type will be the handler? 正如您所看到的,问题出在Logger类中:处理程序将是什么类型?

I can't really create an interface because of virtual template functions: 由于虚拟模板功能,我无法真正创建接口:

class IHandler
{
public:
    virtual ~IHandler() = default;

    template <class T>
    virtual void log(const T& tolog) = 0; // ERROR templated virtual function!
}

Need help 需要帮忙

I want the IHandler::tolog function to be templated because I want to use the power of the operator<< for both ostream and qDebug() . 我希望IHandler::tolog函数是模板化的,因为我想使用operator<< IHandler::tolog功能来同时使用ostreamqDebug() And I don't want to reimplement all the overloads myself (long list for ostream , even longer for qDebug !). 而且我不想自己重新实现所有重载( ostream的长列表, qDebug甚至更长!)。

I want to achieve it, no matter how (lambda functions with auto ?)... Any suggestions welcome (I may do something wrong here?). 我想实现它,无论如何(lambda函数与auto ?)...任何建议欢迎(我可能在这里做错了什么?)。

Thanks :) 谢谢 :)

It's obviously not possible to have templated virtual functions, but you could use type erasure to "erase" the concrete type, so you don't need a template anymore. 显然不可能有模板化的虚函数,但你可以使用类型擦除来“擦除”具体类型,因此你不再需要模板了。

Basically, you create an interface ILoggableValue that knows how to log your values both using QDebug and std::ostream streams, and use templates to generate concrete implementations for different types: 基本上,您创建了一个接口ILoggableValue ,它知道如何使用QDebugstd::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;
};

Then, you create IHandler the same way you suggested it, but now you can use ILoggableValue to erase the template: 然后,按照建议的方式创建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());
    }
};

Finally, you use IHandler in your Logger : 最后,在Logger使用IHandler

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 is a live example. 是一个实例。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM