C++中的双重调度

问题描述 投票:0回答:1

我需要以下类型的调度函数。 在我的应用程序中,我真正拥有的是一个指向状态基类(foo)的指针,以及一个指向测量基类(bar)的指针。 根据传递给dispatch2函数的派生实例是什么,我需要产生一个给定状态的估计测量值。 例如,派生状态类可以是位置,派生测量类可以是ToF(飞行时间)。 然后,处理程序将从f中获取状态信息(例如发射器位置),以及从b中获取采集器信息(例如传感器位置),并计算给定的期望ToF。 然后将其返回,并可与实际测量值(b)进行比较。

string dispatch2(foo* f, bar* b) {
  if      ( dynamic_cast<Foo>(f) )  return foo1(f,b);
  else if ( dynamic_cast<FOo>(f) )  return foo2(f,b);
  else if ( dynamic_cast<FOO>(f) )  return foo3(f,b);
  throw std::runtime_error("dispatch for f not defined");
}

string foo1(foo* f, bar* b) {
  if      ( dynamic_cast<Bar>(b) )  return foo1bar1handler(f,b);
  else if ( dynamic_cast<BAR>(b) )  return foo1bar2handler(f,b);
  throw std::runtime_error("foo1: dispatch for b not defined");
}

string foo2(foo* f, bar* b) {
  if      ( dynamic_cast<Bar>(b) )  return foo2bar1handler(f,b);
  else if ( dynamic_cast<BAR>(b) )  return foo2bar2handler(f,b);
  throw std::runtime_error("foo2: dispatch for b not defined");
}

string foo3(foo* f, bar* b) {
  if      ( dynamic_cast<Bar>(b) )  return foo3bar1handler(f,b);
  else if ( dynamic_cast<BAR>(b) )  return foo3bar2handler(f,b);
  throw std::runtime_error("foo3: dispatch for b not defined");
}

string foo1bar1handler(foo* f, bar* b) {return "FooBar";}
string foo2bar2handler(foo* f, bar* b) {return "FooBAR";}
string foo3bar1handler(foo* f, bar* b) {return "FOoBar";}
string foo2bar2handler(foo* f, bar* b) {return "FOoBAR";}
string foo2bar1handler(foo* f, bar* b) {return "FOOBar";}
string foo2bar2handler(foo* f, bar* b) {return "FOOBAR";}

显然,没有办法绕过为我想显式处理的每个组合定义终端方法的需求。 但是,我正在寻找其他的方法来实现这个问题。 理想情况下,一些模式可以让用户显式注册每个处理程序,任何没有被处理的组合都可以抛出一个运行时异常。 如果有任何建议,我们将不胜感激。 谢谢你的建议。

c++ design-patterns dispatch
1个回答
1
投票

一种方法(当然不是唯一的方法)是在foo上调用一个虚拟函数,并将bar传递给它。 foo的每个派生类型都以同样的方式实现这个派发函数,它将把自己传递给bar的虚拟处理函数。当你需要添加更多的时候,你扩展接口以接受新的类型。 所有的foo函数都有相同的实现,但它们不同的是,"this "是正确的对象的动态类型。

另外,Andrei Alexandrescu在他的书(现在已经不那么现代了)中对这个问题进行了很好的调查,并提出了一些设计方案。现代C++设计,这本书仍然涵盖了这个思想,但是是为c++98写的,但绝对还是值得一读的(虽然它说的很多不能做的事情现在已经是c++的一部分了,部分原因是那本书)。

现场看吧。https:/godbolt.orgzoRyVJx。

本例有3个Foo类,2个Bar类。

#include <iostream>

class BarBase;

class FooBase { 
public:
    virtual ~FooBase() = default;
    virtual void dispatch(BarBase*) = 0;
};

class Foo1;
class Foo2;
class Foo3;

class BarBase {
public:
    virtual ~BarBase() = default;
    virtual void accept(Foo1*) = 0;
    virtual void accept(Foo2*) = 0;
    virtual void accept(Foo3*) = 0;
};

class Bar1 : public BarBase {
public:
    void accept(Foo1*) override;
    void accept(Foo2*) override;
    void accept(Foo3*) override;
};

class Bar2 : public BarBase {
public:
    void accept(Foo1*) override;
    void accept(Foo2*) override;
    void accept(Foo3*) override;
};

class Foo1 : public FooBase {
public:
    void dispatch(BarBase* bar) override { bar->accept(this); }
};

class Foo2 : public FooBase {
public:
    void dispatch(BarBase* bar) override { bar->accept(this); }
};

class Foo3 : public FooBase {
public:
    void dispatch(BarBase* bar) override { bar->accept(this); }
};

void Bar1::accept(Foo1 * f) { std::cout << "Bar1 accepting Foo1\n"; }
void Bar1::accept(Foo2 * f) { std::cout << "Bar1 accepting Foo2\n"; }
void Bar1::accept(Foo3 * f) { std::cout << "Bar1 accepting Foo3\n"; }
void Bar2::accept(Foo1 * f) { std::cout << "Bar2 accepting Foo1\n"; }
void Bar2::accept(Foo2 * f) { std::cout << "Bar2 accepting Foo2\n"; }
void Bar2::accept(Foo3 * f) { std::cout << "Bar2 accepting Foo3\n"; }

//
// Doesn't know which types of foo and bar it has, but it doesn't matter...
//
void call(FooBase& foo, BarBase& bar) {
    foo.dispatch(&bar);
}

int main() {
    Foo1 f1;
    Foo2 f2;
    Foo3 f3;
    Bar1 b1;
    Bar2 b2;

    call(f1, b1);
    call(f2, b1);
    call(f3, b1);
    call(f1, b2);
    call(f2, b2);
    call(f3, b2);
}

输出。

Bar1 accepting Foo1
Bar1 accepting Foo2
Bar1 accepting Foo3
Bar2 accepting Foo1
Bar2 accepting Foo2
Bar2 accepting Foo3
© www.soinside.com 2019 - 2024. All rights reserved.