基于策略的设计 - 有条件地使用 Base class(函数)

Policy Based Design - Conditional use of Base class (functions)

在来自 wikipedia 的基于策略的设计的 hello world 示例中,我们有通过 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

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

但是,如果 Message 未在 HelloWorld Class 中实现,您如何启用此默认值并在其他情况下丢弃它?

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 语法。

您可以将模板参数设置为默认值,如果用户未提供第二个模板参数,class 将使用您的 DefaultLanguagePolicy。这符合您的目的吗?

`DefaultLanguagePolicy` 必须在 class `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();
}

如果可以在项目中设置设计决策,则每个策略都派生自默认策略——如:

class SomeLangaugePolicy : public DefaultLanguagePolicy { ... };

然后 class 中没有任何更改 HelloWorld - 我的意思是这个 class.

的第一个版本

否则 - 使用来自 <type_traits>:

的工具

使用std::void_t

可以检测给定的class是否有消息功能
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 可以 select 类型取决于 Message 存在:

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

并且需要在您的 class 中进行修改:

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;
};

工作Demo

现在很容易对 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;
};