"动态"没有模板或向下转换的参数类型(?)

时间:2016-03-04 11:42:41

标签: c++ templates design-patterns casting

我有fancyFunction,其中包含一组实现接口A的元素。该函数基于通过接口A读取的属性对这些元素进行复杂的分析。在此分析过程中,它将调用Consumer c的方法,这些方法将元素作为参数。

Consumer旨在获取与A完全无关的特定类型的参数。

你可以想象A是图中边的抽象。该图表在fancyFunction中进行分析,并且 - 例如 - 每次函数"越过"边缘,它会将该边缘发送到Consumer,它会打印存储在边缘中的与边缘无关的附加信息。

下面给出的代码当然不能用类型语言(特别是C ++)编译,但是省略了类型(Matlab,Python),代码就可以了。

为了使它以类型语言(特别是C ++)工作,我看到两个选项:

  1. 将函数声明为

    template <class CONSUMER> void fancyFunction(A[] setOfAs, CONSUMER c){ ... }

  2. 声明operation1operation2以获取最常规的对象,然后在实现中执行向下转发。

  3. 在这种情况下,您建议做什么?(据我所知,访客模式不是一种选择。)

    完整的代码大纲(我暂时没有使用C ++,所以请原谅是否存在轻微的语法错误。):

    void fancyFunction(A[] setOfAs, Consumer* c){
      // do fancy analysis of setOfAs by properties
      // read through interface A
    
      double x = setOfAs[i]->getX();
    
      // call functions in c with arguments of setOfAs[j]
      ...
      c->operationX(setOfAs[i]);
      ...
      c->operationY(setOfAs[j]);
      ...
    }
    
    class A{
      virtual double getX();
    }
    
    class Consumer{
      virtual void operationX(??? x); // whoops, what type do we expect?
      virtual void operationY(??? y); // whoops, what type do we expect?
    }
    
    class Consumer1{
      void operationX(Obj1 x){ ... } // whoops, override with different type
      void operationY(Obj1 y){ ... } // whoops, override with different type 
    }
    
    class Consumer2{
      void operationX(Obj2 x){ ... } // whoops, override with different type
      void operationY(Obj2 y){ ... } // whoops, override with different type
    }
    
    class Obj1 : public A {};
    class Obj2 : public A {};
    
    void test(){
    
        Obj1 o1[];
        Obj2 o2[];
    
        Callback1 c1;
        Callback2 c2;
    
        fancyFunction(o1, &c1);
        fancyFunction(o2, &c2);
    }
    

2 个答案:

答案 0 :(得分:2)

我相信您正在寻找的解决方案称为Visitor Pattern

您不希望在您的花哨功能中手动转换对象A的每个实例,因为这是一个维护噩梦和清晰的代码嗅觉

另一方面,如果每个对象自动处理自己的转换怎么办?这就是访客模式。

首先在基类(A)中定义一个新的“访问”函数,将您的消费者作为唯一参数:

class A
{
public:
    virtual void Visit(Consumer& consumer) = 0;
}

然后为每个继承的类实现此函数,因此:

class B : public A
{
public:
    void Visit(Consumer& consumer)
    {
        consumer.DoOperation(this); // 'this' utomatically resolves to type B*
    }
}

每个派生类型现在通过将“this”指针传递给提供的Consumer实例来处理调用适当的操作重载。 'this'指针会自动解释为最具体的类型。

回顾一下原始的示例代码,看起来每个Consumer都提供多个操作,并且只处理一个类型。 模式可能需要您稍微更改此范例:为每个操作创建单个使用者,其中每个使用者为每个可能的继承提供重载类型。

class ConsumerX
{
public:
    void DoOperation(A* a) { /* ERROR!  This is a base type.  If this function is called, you probably need to implement another overload. */ }
    void DoOperation(B* b) { /* Much better */ }
}

class ConsumerY
{
public:
    void DoOperation(A* a) { /* ERROR!  This is a base type.  If this function is called, you probably need to implement another overload. */ }
    void DoOperation(B* b) { /* Much better */ }
}

然后你的实现循环看起来像这样:

ConsumerX consumerX; // Does Operation X for every type
ConsumerY consumerY; // Does Operation Y for every type
for(int x = 0; x < numElements, x++)
{
    auto element = setOfAs[x];
    element.Visit(consumerX); //Do operation X
    element.Visit(consumerY); //Do operation Y
}

答案 1 :(得分:0)

显然,模板是合适的。我甚至会问你为什么fancyFunction坚持基类A。它应该只是一个开始和结束迭代器。我也不打扰消费者。也要灵活,只需要任何功能。

事实上,我甚至不会写fancyFunction。它已经存在:

std::for_each(o1.begin(), o1.end(),
   [c1](Obj1 o) { double x = o.getX(); c1.operationX(o); c1.operationY(o); }
);