Итерации по наследованию классов в C ++ - PullRequest
1 голос
/ 29 мая 2020

Предположим, у меня есть архитектура некоторых классов (количество классов растет во время разработки), что каждый класс наследуется от N классов с одним и тем же базовым интерфейсом c. Как лучше всего (если возможно) создать базовую функцию (в базовом классе ИЛИ в производном классе), которая будет перебирать наследования?

Цель: Избегайте ошибок разработчиков и убедитесь, что мы не забудем вызвать все базовые функции из всех наследований и сделаем код более понятным и понятным.

См. примечания к редактированию для обновленного состояния

Краткий пример:

class shared_base {
public:
    virtual void func() = 0;
}

class base_1 : virtual public shared_base {
public:
    void func() override {}
}

class base_2 : virtual public shared_base {
public:
    void func() override {}
}

class target : virtual public base_1, virtual public base_2 {
public:
    void func() override {
        // Instead of:
        base_1::func();
        base_2::func();
        // ... My func() implementation
        /*
        ~~TODO~~
        for_each(std::begin(inheritances), std::end(inheritances), [](auto& inheritance) -> void { inheritance::func(); })
        ~~TODO~~
        */
    }
}

Более наглядный и практический пример:

class base {
public:
    virtual void func() = 0;
    /*...Some interface (pure virtual) functions...*/
}

class base_core : virtual public base {
public:
    void func() override {}
    /*...Some base implementations for the rest...*/

protected:
    template <typename FuncT>
    virtual void iterate_over_base_core_inheritances(FuncT function_to_apply) {
        /*~~TODO~~*/
    }
}

template <class Decorator = base_core, typename = typename std::enable_if<std::is_base_of<base_core, Decorator>::value>::type>
class core_1 : virtual public Decorator {
public:
    void func() override {
        // Will iterate (once) over Decorator
        /*iterate_over_base_core_inheritances([](core_base*) -> void {
            // Implementation
        });*/
        // Instead of:
        Decorator::func();
    }
    /*More functions implementations*/
}

template <class Decorator = base_core, typename = typename std::enable_if<std::is_base_of<base_core, Decorator>::value>::type>
class core_2 : virtual public core_1<>, virtual public Decorator {
public:
    void func() override {
        // Will iterate (twice) over core_1 and Decorator
        /*iterate_over_base_core_inheritances([](core_base*) -> void {
            // Implementation
        });*/
        // Instead of:
        Decorator::func();
        core_1::func();
        //... Self func() implementation
    }
    /*More functions implementations*/

protected:
    // If it's not possible doing it in the upper hierarchy level is it possible do it here?
    template <typename FuncT>
    void iterate_over_base_core_inheritances(FuncT function_to_apply) override {
        /*~~TODO~~*/
    }
}

Некоторые вещи, которые нужно знать :

  • Я работаю на платформе Linux 64x (Ubuntu 16.04) - если это важно для ответов.
  • Идея этого кода - создать своего рода Decorator DP, который будет легко расширять и понимать, а также позволит разработчикам использовать protected функции / атрибуты базового класса.

A практический пример (для моего фактического использования) можно найти в this commit .

Изменить:

Благодаря @ RaymondChen У меня есть рабочее решение, с (пока) только одним второстепенным Проблема: каждый раз, когда я хочу использовать класс, реализованный таким образом, мне нужно указать класс core_base в его списке аргументов шаблона (раньше - я использовал параметр типа по умолчанию). Я ищу способ решить эту проблему.
Текущее решение:

template <class ...Decorators>
class core_2 : virtual public Decorators... {
public:
    static_assert((std::is_base_of<base_core, Decorators>::value && ...), "All decorators must inherit from base_core class.");

    void func() override {
        (Decorators::func(), ...);
        //... Self func() implementation
    }
    /*More functions implementations*/
}

Создание примера экземпляра:
Текущее:
std::shared_ptr<base> base = std::make_shared<core_2<core_1<base_core>, core_3<base_core>>>();
Желательно:
std::shared_ptr<base> base = std::make_shared<core_2<core_1<>, core_3<>>>();

A практический пример (для моего фактического использования) можно найти в этот коммит .

1 Ответ

0 голосов
/ 30 мая 2020

Благодаря @ RaymondChen Я очень близко подошел к своей исходной цели с помощью следующего решения [ См. Раздел обновлений внизу ]:

template <class ...Decorators>
class core_2 : virtual public Decorators... {
public:
    static_assert((std::is_base_of<base_core, Decorators>::value && ...), "All decorators must inherit from base_core class.");

    void func() override {
        (Decorators::func(), ...);
        //... Self func() implementation
    }
    /*More functions implementations*/
}

Explanation:

Используя пакет параметров, мы можем создать «список» классов, от которых мы наследуем, а используя выражение сворачивания [c ++ 17], мы можем реализовать его всего в нескольких строках кода.

Плюсы по сравнению с моей первоначальной идеей:

  • Строка создания объекта теперь более понятна и логична:
    Раньше:
    std::shared_ptr<base> base = std::make_shared<core_2<core_1<core_3<>>>>();
    После:
    std::shared_ptr<base> base = std::make_shared<core_2<core_1<base_core>, core_3<base_core>>>();
    Поскольку core_1 и core_3 независимы, но core_2 использует их оба.
  • Нет необходимости в новой функции в базе / производный класс, он просто вписывается в целевую строку (например, в функции is_equal, которая не упоминается в этом сообщении).

Утраченная функциональность:

  • Проверка шаблона is_base_of (Решено с помощью static_assert & fold expressions).
  • Наследование по умолчанию в случае, если не указанное наследование еще невозможно (все еще пытается решить).
    Текущий:
    std::shared_ptr<base> base = std::make_shared<core_2<core_1<base_core>, core_3<base_core>>>();
    Желаемый:
    std::shared_ptr<base> base = std::make_shared<core_2<core_1<>, core_3<>>>();

Обновить

После Много исследований и попыток, я придумал следующее решение (улучшенное также с помощью функции C ++ 20 concepts):

template <class T>
        concept Decorator = std::is_base_of_v<base_core, T>;

class empty_inheritance {};

template<typename Base = base_core, typename ...Decorators>
struct base_if_not_exists {
    static constexpr bool value = sizeof...(Decorators);
    using type = typename std::conditional<value, empty_inheritance, Base>::type;
};

template <Decorator ...Decorators>
class core_2 : virtual public base_if_not_exists<base_core, Decorators...>::type, virtual public Decorators... {
public:
    void func() override {
        if constexpr (!base_if_not_exists<base_core, Decorators...>::value) {
            base_core::func();
        }
        (Decorators::func(), ...);
        //... Self func() implementation
    }
    /*More functions implementations*/
}

Функциональность не потеряна :)

...