C++双重调度

Double Dispatch in C++

本文关键字:调度 C++      更新时间:2023-10-16

我需要以下类型的调度功能。 在我的应用程序中,我真正拥有的是指向状态基类 (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";}

显然,没有办法解决为我想显式处理的每个组合定义结束方法的需求。 但是,我正在寻找实现这一点的替代方法。 理想情况下,某些允许用户显式注册每个处理程序的模式,以及未处理的任何组合都可能引发运行时异常。 任何建议将不胜感激。 谢谢

一种方法(当然不是唯一的方法(是在 foo 上调用一个虚函数,传递它吧。 每个派生类型的 foo 都以相同的方式实现此调度函数,它将自身传递给 bar 中的虚拟处理程序函数。 当您需要添加更多接口时,您可以扩展接口以接受新类型。 所有 foo 函数都有相同的实现,但它们有所不同,因此"this"是正确的对象的动态类型。

此外,Andrei Alexandrescu在他的(现在不那么现代的(书《现代C++设计》中对此进行了很好的研究,该书仍然涵盖了这个想法,但为c ++ 98编写,但绝对仍然值得一读(尽管它说许多不能做的事情现在是C++的一部分,部分原因是这本书。

现场观看:https://godbolt.org/z/oRyVJx

此示例有 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 Foo1n"; }
void Bar1::accept(Foo2 * f) { std::cout << "Bar1 accepting Foo2n"; }
void Bar1::accept(Foo3 * f) { std::cout << "Bar1 accepting Foo3n"; }
void Bar2::accept(Foo1 * f) { std::cout << "Bar2 accepting Foo1n"; }
void Bar2::accept(Foo2 * f) { std::cout << "Bar2 accepting Foo2n"; }
void Bar2::accept(Foo3 * f) { std::cout << "Bar2 accepting Foo3n"; }
//
// 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