Как реализовать кучу похожих классов

Моя задача — определить класс спецификации устройства, который состоит из различного количества свойств. Например, спецификация Устройство1 содержит Свойство1, Свойство2. Некоторые другие Device2 содержат только Property1, спецификация DeviceX содержит Property5, Property6 и т. д.

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

Существует множество очень похожих классов, содержащих специализированные методы, использующие идиому CRTP. CRTP используется для доступа к методу setParameter класса SPECIFICATION:

template<typename SPECIFICATION>
struct Property1
{
    void setProperty1( const double & Value)
    {
        static_cast<SPECIFICATION*>(this)->setParameter( "Property1", Value);
    }
};

template<typename SPECIFICATION>
struct Property2
{
    void setProperty2( const double & Value)
    {
        static_cast<SPECIFICATION*>(this)->setParameter( "Property2", Value);
    }
};

Эти шаблонные классы затем используются в отношении идиомы CRTP:

template<typename DEVICE_TYPE>
class Device
{
public:
        class Specification;
};

template<>
class Device<DeviceTypeAAA>::Specification
        :   public Property1<Specification>,
            public Property2<Specification>
{
};

template<>
class Device<DeviceTypeBBB>::Specification
        :   public Property1<Specification>
{
};

template<>
class Device<DeviceTypeCCC>::Specification
        :   public Property2<Specification>
{
};

Как видно из приведенного выше фрагмента кода, спецификация устройства «собирается» с использованием наследования требуемых свойств. Все устройства являются специализированными классами Device::Specification.

ВОПРОС: Поскольку существует действительно много «классов свойств», реализующих методы «setProperty», и довольно много классов спецификаций устройств, мне любопытно узнать, существует ли какой-нибудь элегантный метод, как реализовать эту группу похожих классов более эффективным кодом. Я думал об использовании boost::mpl::vector в сотрудничестве с линейным наследованием boost, чтобы определить список всех свойств, которые должно получить устройство... Может быть, я следую совершенно неправильному направлению, реализуя это. Пожалуйста, дайте мне знать, если вы так думаете.

Если есть идеи как оптимизировать реализацию, буду очень рад. Большое спасибо всем, кто готов помочь!


person Martin Kopecký    schedule 26.04.2016    source источник


Ответы (1)


В приведенной ниже реализации используется шаблон с переменным числом переменных, но это приводит к возможно длинному стеку наследования, но это не должно быть проблемой.

За исключением этого, он выполняет свою работу, сводя определение спецификации к одному объявлению наследования со списком свойств.

Он компилируется и хорошо работает на GCC 4.7 (C++11) и более поздних версиях, а также на clang 3.8.

#include <iostream>
#include <string>

template<typename SPECIFICATION>
struct Property1
{
    void setProperty1( const double & Value)
    {
        static_cast<SPECIFICATION*>(this)->setParameter( "Property1", Value);
    }
};

template<typename SPECIFICATION>
struct Property2
{
    void setProperty2( const double & Value)
    {
        static_cast<SPECIFICATION*>(this)->setParameter( "Property2", Value);
    }
};

template<typename SPECIFICATION, template<typename> class ... PROPERTIES>
struct Properties; // primary-template

template <typename SPECIFICATION, 
          template<typename> class PROPERTY, 
          template<typename> class ... PROPERTIES>
struct Properties<SPECIFICATION, PROPERTY, PROPERTIES...> : 
       PROPERTY<SPECIFICATION>, 
       Properties<SPECIFICATION, PROPERTIES...>{}; // head specialisation 

template <typename SPECIFICATION, 
          template<typename> class PROPERTY>
struct Properties<SPECIFICATION, PROPERTY> : 
       PROPERTY<SPECIFICATION>{}; // tail specisalization

template<typename DEVICE_TYPE>
class Device
{
public:
        class Specification;
};

struct DeviceTypeAAA{};
struct DeviceTypeBBB{};
struct DeviceTypeCCC{};

template<>
class Device<DeviceTypeAAA>::Specification
        :   public Properties<Specification, Property1, Property2>
{
    public:
    void setParameter(const std::string &name, double value){
        std::cout << "DeviceTypeAAA: "  << name << " = " << value << "\n";
    }
};

template<>
class Device<DeviceTypeBBB>::Specification
        :   public Properties<Specification, Property1>
{
    public:
    void setParameter(const std::string &name, double value){
        std::cout << "DeviceTypeBBB: "  << name << " = " << value << "\n";
    }
};

template<>
class Device<DeviceTypeCCC>::Specification
        :   public Properties<Specification, Property2>
{
    public:
    void setParameter(const std::string &name, double value){
        std::cout << "DeviceTypeCCC : "  << name << " = " << value << "\n";
    }
};


int main()
{
  Device<DeviceTypeAAA>::Specification a{};
  Device<DeviceTypeBBB>::Specification b{};
  Device<DeviceTypeCCC>::Specification c{};

  a.setProperty1(2.);
  a.setProperty2(2.);

  b.setProperty1(2.);
  b.setProperty2(2.); // compiler error, no member setProperty2

  c.setProperty1(2.);  // compiler error setProperty1
  c.setProperty2(2.);


}
person Guillaume Fouillet    schedule 27.04.2016