【问题标题】:Compile-time type based dispatch基于编译时类型的调度
【发布时间】:2010-09-13 20:00:59
【问题描述】:

遵循“现代 C++ 设计”中的技术,我正在实现一个具有各种编译时优化的持久性库。如果该变量派生自给定类,我希望能够将函数分派给模板化成员变量:

template<class T, template <class> class Manager = DefaultManager> class Data
{
private:
   T *data_;

public:
   void Dispatch()
   {
      if(SUPERSUBCLASS(Container, T))
      {
         data_->IKnowThisIsHere();
      }
      else
      {
         Manager<T>::SomeGenericFunction(data_);
      }
   }
}

其中 SUPERSUBCLASS 是用于确定对象继承的编译时宏。当然,在 T 确实从 Container 继承(或 T 是内在类型等)的所有情况下,这都会失败,因为编译器正确地抱怨 IKnowThisIsHere() 不是数据成员,即使永远不会遵循此代码路径,使用 T = int 进行预处理后如下所示:

private:
   int *data_;

public:
   void Dispatch()
   {
      if(false)
      {
         data_->IKnowThisIsHere();

编译器清楚地抱怨这段代码,即使它永远不会被执行。使用 dynamic_cast 的建议也不起作用,因为在编译时再次尝试了不可能的类型转换(例如 T=double, std::string):

void Dispatch()
   {
      if(false)
      {
         dynamic_cast<Container*>(data_)->IKnowThisIsHere();

error: cannot dynamic_cast '((const Data<double, DefaultManager>*)this)->Data<double, DefaultManager>::data_' (of type 'double* const') to type 'class Container*' (source is not a pointer to class)
error: cannot dynamic_cast '((const Data<std::string, DefaultManager>*)this)->Da<sttad::string, DefaultManager>::data_' (of type 'struct std::string* const') to type 'class Container*' (source type is not polymorphic)

如果 T 确实从 Container 继承,我真的需要模仿(或者确实说服!)让编译器发出一组代码,如果不是,则发出另一组代码。

有什么建议吗?

【问题讨论】:

    标签: c++ templates


    【解决方案1】:

    正如 Alexandrescu 在他的《现代 C++ 设计》一书中提出的,重载对于实现编译时调度很有用。

    您可以使用这样的类在编译时将布尔值或整数转换为类型:

    template <bool n>
    struct int2type
    { enum { value = n}; };
    

    以下源代码显示了一个可能的应用:

    #include <iostream>
    
    #define MACRO()   true  // <- macro used to dispatch 
    
    template <bool n>
    struct int2type
    { enum { value = n }; };
    
    void method(int2type<false>)
    { std::cout << __PRETTY_FUNCTION__  << std::endl; }
    
    void method(int2type<true>)
    { std::cout << __PRETTY_FUNCTION__  << std::endl; }
    
    int
    main(int argc, char *argv[])
    {
        // MACRO() determines which function to call
        //
    
        method( int2type<MACRO()>()); 
    
        return 0;
    }
    

    当然,真正完成这项工作的是 MACRO() 或作为元函数的更好实现

    【讨论】:

      【解决方案2】:

      您需要一种编译时if。然后根据 true 的情况调用一个函数。这样,编译器就不会偶然发现它无法编译的代码(因为它被安全地存储在另一个永远不会被实例化的函数模板中)。

      有几种方法可以实现这样的编译时间if。最常见的是使用 SFINAE 成语:substitution failure is not an error。 Boost 的is_base_of 实际上就是这个成语的一个实例。要正确使用它,您不会将其写在 if 表达式中,而是将其用作函数的返回类型。

      未经测试的代码:

      void Dispatch()
      {
          myfunc(data_);
      }
      
      private:
      
      // EDIT: disabled the default case where the specialisation matched
      template <typename U>
      typename enable_if_c<is_base_of<Container, U>::value, U>::type myfunc(U& data_) {
          data_->IKnowThisIsHere();
      }
      
      template <typename U>
      typename disable_if_c<is_base_of<Container, U>::value, U>::type myfunc(U& data_) { // default case
          Manager<U>::SomeGenericFunction(data_);
      }
      

      【讨论】:

      • 这种方法会导致不明确的重载。当 is_base_of 元函数成功时,您应该让编译器放弃第二种方法(默认情况)。
      • 我在代码中删除了第一个匹配时的默认大小写。不确定是否正确
      【解决方案3】:

      Boost traits 有一些作用:is_base_of

      【讨论】:

        【解决方案4】:

        查看 boost 模板元编程库。此外,根据您要完成的任务查看 boost 序列化库,因为它可能已经包含您需要的内容。

        【讨论】:

          【解决方案5】:

          不幸的是,我也经历过这种情况(它也是一个运行时调用;))如果您以与以前类似的方式传入非多态或类类型,编译器会抱怨:

          error: cannot dynamic_cast '((const Data<double, DefaultManager>*)this)->Data<double, RawManager>::data_' (of type 'double* const') to type 'class Container*' (source is not a pointer to class)
          

          error: cannot dynamic_cast '((const Data<std::string, DefaultRawManager>*)this)->Data<std::string, DefaultManager>::data_' (of type 'struct std::string* const') to type 'class Container*' (source type is not polymorphic)
          

          【讨论】:

            【解决方案6】:

            我对“从第一原则”做这件事很感兴趣,因为这是一种教育好奇心。不过,我会看看 Boost 库。

            无论如何,我认为 is_base_of 没有任何帮助 - 它与 SUPERSUBCLASS 宏完全相同...

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 2019-02-05
              • 2019-06-19
              • 2014-06-17
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多