Проектирование на основе политик — условное использование базового класса (функций)

В приветственном примере дизайна, основанного на политике, из википедии у нас есть очень хороший способ определения конкретных функций-членов реализации (политики) с помощью директив using:

template <typename OutputPolicy, typename LanguagePolicy>
class HelloWorld : private OutputPolicy, private LanguagePolicy {
public:
// Behavior method.
void Run() const {
   // Two policy methods.
   Print(Message());
}

private:
using LanguagePolicy::Message;
using OutputPolicy::Print;
};

Теперь предположим, что вы хотите, чтобы пользователь забыл реализовать метод Message в языковой политике — может быть много других, которые пользователь реализовал. Следовательно, вы определяете резервный класс

class DefaultLanguagePolicy {
protected:
  std::string Message() const { return ""; }
  // and fallbacks for other methods a user may not have implemented
}

Но как включить это значение по умолчанию, если Message не реализовано в классе HelloWorld, а в противном случае отбросить его?

template <typename OutputPolicy, typename LanguagePolicy, typename DefaultLanguagePolicy>
class HelloWorld : private OutputPolicy, private LanguagePolicy, private DefaultLanguagePolicy {
public:
// Behavior method.
 void Run() const {
   // Two policy methods.
   Print(Message());
 }

 private:
 // what do I need to write to get this kind of behaviour:
 if (LanguagePolicy has Message implemented) {
    using LanguagePolicy::Message;
 } else {
    using DefaultLanguagePolicy::Message;
 }
   using OutputPolicy::Print;
 };

Спасибо за любые предложения, которые в идеале позволяют сохранить этот красивый синтаксис using.

Почему в Python есть оператор &quot;pass&quot;?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Коллекции в Laravel более простым способом
Коллекции в Laravel более простым способом
Привет, читатели, сегодня мы узнаем о коллекциях. В Laravel коллекции - это способ манипулировать массивами и играть с массивами данных. Благодаря...
JavaScript Вопросы с множественным выбором и ответы
JavaScript Вопросы с множественным выбором и ответы
Если вы ищете платформу, которая предоставляет вам бесплатный тест JavaScript MCQ (Multiple Choice Questions With Answers) для оценки ваших знаний,...
Массив зависимостей в React
Массив зависимостей в React
Все о массиве Dependency и его связи с useEffect.
Toor - Ангулярный шаблон для бронирования путешествий
Toor - Ангулярный шаблон для бронирования путешествий
Toor - Travel Booking Angular Template один из лучших Travel & Tour booking template in the world. 30+ валидированных HTML5 страниц, которые помогут...
1
0
124
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

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

DefaultLanguagePolicy должен быть определен перед классом HelloWorld.

 class DefaultLanguagePolicy {
   protected:
   std::string Message() const { return "Please add a language policy\n"; }
 };

template <typename OutputPolicy, typename LanguagePolicy = DefaultLanguagePolicy>
 class HelloWorld : private OutputPolicy, private LanguagePolicy {
 public:
  void Run() const {
       Print(Message());
     }

Тестовый запуск напечатает «Пожалуйста, добавьте языковую политику», если языковая политика не указана.

int main() {
    typedef HelloWorld<OutputPolicyWriteToCout> myDeafult;
    myDeafult forgetLanguage;
    forgetLanguage.Run();

    HelloWorld<OutputPolicyWriteToCout, LanguagePolicyGerman> InGerman;
    InGerman.Run();
}

Спасибо за быстрый ответ. Это возможно, но, насколько я понимаю, все методы в классе LanguagePolicy будут перезаписаны значениями по умолчанию, не так ли? Я ищу способ вызывать значение по умолчанию только тогда, когда конкретный метод не реализован.

J Reichardt 18.12.2020 19:47

Нет. Если указан второй параметр. Он будет использован. Пожалуйста, проверьте это.

ytlu 18.12.2020 19:48

Боюсь, пример слишком прост. Предположим, что LanguagePolicy имеет более одного метода. Все, кроме Message, реализованы, поэтому только Message нужно заполнить значением по умолчанию. Установка параметра шаблона по умолчанию, как вы предлагаете, перезапишет все конкретные реализации LanguagePolicy значениями по умолчанию, не так ли? Я отредактирую вопрос, чтобы сделать это более ясным.

J Reichardt 18.12.2020 19:56

@JReichardt Нет. Он не перезапишет вывод на другом языке. В последнем примере я использую имя объекта HellowWorld. Это вызывает проблемы в сочетании с именем класса. Я изменил имя переменной в версии eidted. Теперь это работает.

ytlu 18.12.2020 20:04

Я считаю, что мы неправильно понимаем нас. Насколько я понимаю, ваше решение обеспечивает значение по умолчанию для всей реализации LanguagePolicy. Это все или ничего. Вместо этого я ищу способ предоставить значения по умолчанию только для определенных методов LanguagePolicy, которые могут отсутствовать. Сам класс реализован - просто не со всеми функциями HelloWorld хотелось бы его вызвать. Это проясняет ситуацию? В очередной раз благодарим за помощь.

J Reichardt 18.12.2020 20:30

Меня правильно поняли, и по умолчанию другие политики не перезаписываются. Просто запустите мой новый файл main(). И, конечно же, вы должны добавить другие политики, которые я не включил в свой пост.

ytlu 18.12.2020 22:02
Ответ принят как подходящий

Если в проекте можно установить проектное решение, что каждая политика вытекает из политики по умолчанию, например:

class SomeLangaugePolicy : public DefaultLanguagePolicy { ... };

Тогда в классе нечего менять HelloWorld - я имею в виду первую версию этого класса.


В противном случае - используйте инструменты из <type_traits>:

Определить, есть ли у данного класса функция Message, можно с помощью std::void_t

template <typename T, typename = std::void_t<>> 
struct has_message : std::false_type {};
template <typename T> 
struct has_message<T, std::void_t<decltype(&T::Message)>> : std::true_type {};

С этим трейтом has_message, с std::conditional_t ​​ можно выбрать тип в зависимости от наличия Message:

template <typename LanguagePolicy, typename DefaultLanguagePolicy>
using LanguageBase = std::conditional_t<has_message<LanguagePolicy>::value,
                                        LanguagePolicy, 
                                        DefaultLanguagePolicy>;

И необходимые изменения в вашем классе:

template <typename OutputPolicy, typename LanguagePolicy, typename DefaultLanguagePolicy>
class HelloWorld : 
         private OutputPolicy, 
         private LanguageBase<LanguagePolicy, DefaultLanguagePolicy> {
public:
    // Behavior method.
    void Run() const {
        // Two policy methods.
        Print(Message());
    }

private:
    using LanguageBase<LanguagePolicy, DefaultLanguagePolicy>::Message;
    using OutputPolicy::Print;
};

Рабочая Демо

Теперь то же самое легко сделать с OutputPolicy и всеми остальными политиками.

Вы можете определить макрос для определения детекторов политик:

#define DEFINE_HAS_FUNCTION_TRAIT(function) \
template <typename T, typename = std::void_t<>> \ 
struct has_##function : std::false_type {}; \
template <typename T> \
struct has_##function<T, std::void_t<decltype(&T::function)>> : std::true_type {}

Затем:

DEFINE_HAS_FUNCTION_TRAIT(Message); // define has_Message
DEFINE_HAS_FUNCTION_TRAIT(Print); // define has_Print

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

template <typename OutputPolicy, typename LanguagePolicy, typename DefaultLanguagePolicy>
class HelloWorld : 
         private OutputPolicy, 
         private LanguagePolicy, private DefaultLanguagePolicy {
public:
    // Behavior method.
    void Run() const {
        // Two policy methods.
        Print(Message());
    }

private:
    using std::conditional_t<has_Message<LanguagePolicy>::value,
                             LanguagePolicy, 
                             DefaultLanguagePolicy>::Message;
    using OutputPolicy::Print;
};


@PiortrNycz Большое спасибо. Это работает. Однако для этого требуется определить другую черту для наличия/отсутствия каждого метода в LanguagePolicy. Может есть более простой способ? Можно с уверенностью предположить, что LanguagePolicy реализует подмножество DefaultLanguagePolicy, поэтому для каждого метода, отсутствующего в LanguagePolicy, определенно есть один в DefaultLanguagePolicy.

J Reichardt 18.12.2020 20:23

На самом деле, теперь, когда я думаю об этом больше, я вижу другую проблему: вы определяете LanguageBase в зависимости от has_ message — но разве это не непрактично, если в LanguagePolicy есть другие функции, которые могут отсутствовать и нуждаться в значениях по умолчанию, верно?

J Reichardt 18.12.2020 20:37

Таким образом, с определением трейтов has_function для каждой функции, которую LanguagePolicy может реализовать, можно просто опустить определение LanguageBase и поместить std::conditional непосредственно в private: части HelloWorld — это именно то предложение if, которое я искал.

J Reichardt 18.12.2020 20:50

Вы можете определить макрос для обнаружения любой такой «функции». И, конечно, вы можете использовать conditional_t ​​напрямую, однако, ИМХО, проще иметь такие вспомогательные определения типов, как LaungageBase.

PiotrNycz 18.12.2020 21:14

Я предоставил решение для случаев, когда в одном классе политики можно обнаружить больше функций.

PiotrNycz 18.12.2020 21:26

Великолепно! Это так приятно - спец. с макросом. Спасибо! Что касается предложения вывести LanguagePolicy из LanguagePolicyDefault, то это не вариант для меня, это тоже хорошо, но не вариант для меня. В моем случае по умолчанию нужны некоторые вещи (некоторые размерные настройки), которые доступны только из политики не по умолчанию.

J Reichardt 18.12.2020 22:31

Еще один аспект решения с макросами заключается в том, что это делает удобным определение has_function внутри класса, необходимого для проверки доступности закрытых функций-членов. Так что еще раз большое спасибо.

J Reichardt 18.12.2020 22:55

Другие вопросы по теме