全能开发工程师:向前引Forwarders的多方面使用

发布时间:2023-05-19

一、Forwarders的基础概念

在C++中,为了避免代码重复,我们会使用函数指针、虚函数等来实现代码的复用。然而,对于一些比较复杂的应用,这种方式仍然相对繁琐且易错。而在这种情况下,Forwarders就可以起到强有力的作用:它可以在一些对象之间建立联系,从而将对象与该对象下面的所有函数之间相连。 我们使用Forwarders的主要目的,在于实现除去接口的抽象化代码。举个例子,如果之前我们的强制类型转换与函数名的供养方式存在冲突,则我们可以采用以下的解决方案:

class MyClass
{  
  public:  
  int doSomething(double x)  
  {  
    cout << "in MyClass::doSomething(" << x << ")" << endl;  
    return(int)x;  
  }  
};  
class YourClass
{  
  public:  
    int doit()  
    {  
      typedef int (MyClass::*pmf_type)(double);  
      pmf_type pmf = &MyClass::doSomething;  
      MyClass mc;  
      int result = (mc.*pmf)(3.14);  
      cout << "result = " << result << endl;  
      return result;  
    }  
};

而现在,我们只需要使用一个typename参数即可实现。

template<typename T, typename R, typename... Args>    
class Forwarder                 
{                      
  public:                   
  Forwarder(R (T::*pmf)(Args...))  
    : pmf(pmf) {}                     
    R Call(T* Object, Args... args)  
    { return (Object->*pmf)(args...); }  
  private:                  
    R (T::*pmf)(Args...);    
};                            
struct MyClass 
{                          
  int DoSomething(double x) 
  { return(x); }              
};                         
struct YourClass 
{                      
  Forwarder<MyClass, int, double> f2{&MyClass::DoSomething};                        
  int callDoSomething(MyClass* p, double x)                     
  {                     
    return f2.Call(p,x);                      
  }                     
};                                  

值得注意的是,对于嵌套类定义(如MyClass::*)来说,我们需要在参数表中进行一个小小的变形,即使用typedef进行访问,而非直接访问。

二、Forwarders的操作与实现

除了以上简单的应用外,Forwarders还可以拥有更加广泛的应用场景。比如说,在某个函数的传递过程中,如果该函数拥有过多参数,则可以令其使用Forwarders处理参数。 在此时,我们需要令该指针指向一个Forwarder类对象。这个操作看起来比较困难,但实际上代码的实现非常简单,如下所示:

template<typename... Args> 
class Function  
{           
  public:  
    typedef std::function<void(Args...)> HandlerType;  
    void operator+=( HandlerType h )  
    {                                          
      handlers.push_back( h );                  
    }                                          
    void operator()( Args... args ) const      
    {                                          
      for( auto& handler : handlers )           
        handler( args... );                     
    }                                          
  private:                                     
    std::vector<HandlerType> handlers;          
};
template<typename T, typename R, typename... Args>    
class Forwarder                 
{                      
  public:                   
  Forwarder(R (T::*pmf)(Args...))  
    : pmf(pmf) {}                     
    R Call(T* Object, Args... args)  
    { return (Object->*pmf)(args...); }  
  operator typename Function<Args...>::HandlerType()  
  {  
    return [&](Args... args)
               { Call(object, args...); };  
  }  
  private:                  
    T* object;              
    R (T::*pmf)(Args...);    
};                           

在这段代码中,我们首先定义了一个Function类,它能够编写抽象化函数,并在不同的对象之间建立联系。而在Forwarder的实现中,我们需要在里面编写一个左移运算符,将该对象的类型转化为Function类的HandlerType类型即可。

三、Forwarders的注意事项与用途分析

在编写回调函数或者说Adapters函数过程中,我们需要对Forwarders进行左移运算符的操作,而同样需要一定的规范来进行操作。 正如上文所述,我们可以使用Function类和Forwarder类将两个不同的类之间联系起来,实现类的接口化。同时,我们还需要注意构造函数中的typename之间的顺序。 但需要注意的是,虽然Forwarders具有很强的抽象化原则,不过我们在进行实际操作时,还需要考虑它的数据类型转换问题。因为Forwarders会在代码实现中进行许多样式和指针类型的转化,如果不加入安全校验的话,该方案存在许多隐患。

四、Forwarders的优缺点分析

Forwarders在代码的复用以及模板代码的高效展示方面,都具有一定的优势。然而,由于其调用过程较为繁琐,不太容易被用户接受。同时,在数据类型安全方面,它还存在一些潜在的问题。 总体来说,在解决抽象化代码问题以及模板的展示方面,Forwarders具有不可忽视的作用。只不过,对于安全性和程序效率等问题,我们需要在使用的时候进行一定的权衡和考虑。