Полиморфный регистратор (виртуальные шаблонные функции?) - PullRequest
0 голосов
/ 28 мая 2018

КОНТЕКСТ

У меня есть библиотека, используемая несколькими программами.Некоторые из них являются базовой командной строкой, а некоторые имеют пользовательский интерфейс Qt.

Я хочу реализовать уникальный класс журнала внутри этой библиотеки, чтобы любое программное обеспечение могло его использовать.Однако, если мы находимся в среде Qt, я бы хотел использовать функцию qDebug() с конкретным QtMessageHandler.Цель состоит в том, чтобы иметь возможность регистрировать ошибки внутри этой библиотеки, и журналы будут печататься по-разному, в зависимости от того, используется ли библиотека в среде пользовательского интерфейса или нет (библиотека не имеет зависимостей от 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;
}

и для пользовательского интерфейса я хочу использовать qDebug(), чтобы я мог настроить пользовательский QtMessageHandler для печати ошибки в пользовательском интерфейсе и зарегистрировать ее в файле:

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

ПРОБЛЕМА

Как видите, проблема в классе Logger: какого типа будет обработчик?

Iне могу создать интерфейс из-за функций виртуального шаблона:

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 !).

Я хочу достичь этого, нетнезависимо от того, как (лямбда работает с auto?) ... Любые предложения приветствуются (я могу сделать что-то не так?).

Спасибо:)

1 Ответ

0 голосов
/ 28 мая 2018

Очевидно, что невозможно иметь шаблонные виртуальные функции, но вы можете использовать стирание типа, чтобы «стереть» конкретный тип, поэтому вам больше не нужен шаблон.

По сути, вы создаете интерфейс 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;
};

Здесь - живой пример.

...