Псевдоним шаблона более общего шаблона c с использованием внешнего типа шаблона в качестве параметра шаблона - PullRequest
0 голосов
/ 17 января 2020

Я хотел взять четко определенный шаблонный класс, который зависит от boost :: signal2, и абстрагировать его на один уровень c глубже, что могло бы удалить зависимость, позволяя использовать другую реализацию, но все же позволяя более высокий уровень функции, которые будут использоваться в зависимости от более общего c интерфейса вместо более специфического c. В моем рефакторе я полностью застрял. И я уже знаю, что я немного вне своей лиги здесь. Я только начал действительно изучать C ++ в течение этого года. Но это немного весело. Но я кое-что упускаю из своего понимания шаблонов. Для начала библиотека, над которой я работаю, будет использовать C ++ 17, я использую clang ++ (Ma c) и в зависимости от Boost ~ 1.7.2 Signals2.

Рассмотрим следующее выдержки:

#include <boost/signals2.hpp>

template<typename Functional_T, typename Return_T, typename ...Args_T>
using SignalHandler = typename Functional_T::template Functional_T<Return_T(Args_T...)>;

template<typename Connection_T>
struct SignalConnection; //no issues here, just wraps a private Connection_T with a getter/setter

template<typename Connection_T, typename Functional_T, typename Return_T, typename ...Args_T>
struct ISignalEmitter
{
protected:
    virtual Return_T trigger(Args_T ...args) = 0;
public:
    virtual void disconnectAll() = 0;
    virtual SignalConnection<Connection_T> onSignal(SignalHandler<Functional_T, Return_T, Args_T...> &signalHandler) = 0;
    virtual void cancelOnSignal(const SignalConnection<Connection_T> &connection) = 0;
};

Но вот где я полностью застрял. То, что я хочу определить, на мой взгляд, выглядит так:

template<typename Return_T, typename ...Args_T>
using BoostSignals2SignalHandler = SignalHandler<boost::signals2::slot, Return_T, Args_T...>;

И я думаю, что мы все знаем, что происходит. slot требует аргументов шаблона. Так как я могу построить это так, чтобы это работало? Я пробовал разные вещи, но я застрял. Моя следующая догадка:

template<typename Return_T, typename ...Args_T>
using BoostSignals2SignalHandler = SignalHandler<template boost::signals2::slot::template, Return_T, Args_T...>;

Мне не хочется спрашивать, но я знаю, что не могу быть первым человеком, пытающимся разгадать эту загадку. И должен быть способ думать об этих шаблонах таким образом, чтобы он был понятен, а не только предполагал и проверял. У меня есть псевдоним шаблона BoostSignals2SignalHandler, который является специфицированной c версией SignalHandler минус первый аргумент шаблона плюс использование существующего шаблона в качестве спецификационного аргумента шаблона c для более универсального c шаблона.

Я бы спросил: «Это плохой дизайн?», Но я чувствую, что концепция достаточна для решения проблемы, которую я хочу решить. Я просто не понимаю, что именно делать. Может кто-то указать мне верное направление? Опять ненавижу спрашивать. Это была головная боль, выясняющая. Заранее спасибо!

1 Ответ

1 голос
/ 17 января 2020

Вместо перечисления аргументов шаблона как Return_T, Args_T... я бы следовал примеру Signals2 и использовал бы один параметр типа функции, например Return_T(Args_T...) в SignalHandler.

Затем мы просто изменили Functional_T на параметр шаблона шаблона.

template<template <typename> typename Functional_T, typename Signature_T>
using SignalHandler = Functional_T<Signature_T>;

Теперь первый параметр, переданный в SignalHandler, должен быть шаблоном, который может быть установлен одним параметром шаблона. Поскольку вы используете c++17, мы можем также передавать шаблоны с большим количеством параметров, если они имеют значения по умолчанию.

Чтобы получить Return_T и Args_T... в ISignalEmitter, мы используем частичную специализацию.

template<typename Connection_T, template <typename> typename Functional_T, typename Signature_T>
struct ISignalEmitter; // Base declaration to match the specialization against

template<typename Connection_T, template <typename> typename Functional_T, typename Return_T, typename ...Args_T>
struct ISignalEmitter<Connection_T, Functional_T, Return_T(Args_T...)> // Matching Return_T and Args_T against Signature_T
{
protected:
    virtual Return_T trigger(Args_T ...args) = 0;
public:
    virtual void disconnectAll() = 0;
    virtual SignalConnection<Connection_T> onSignal(SignalHandler<Functional_T, Return_T(Args_T...)> &signalHandler) = 0;
    virtual void cancelOnSignal(const SignalConnection<Connection_T> &connection) = 0;
};

На этом этапе вы можете определить BoostSignals2SignalHandler, например,

template<typename Signature_T>
using BoostSignals2SignalHandler = SignalHandler<boost::signals2::slot, Signature_T>;

using MySignalHandler = BoostSignals2SignalHandler<void()>;
...