C ++动态调度功能

时间:2020-06-27 05:38:58

标签: c++ c++11 polymorphism dispatch

我正在尝试创建一个将以对象的动态类型调用的重载函数。我尝试这样做,而不会干扰下面的实际类结构,因为我没有直接访问权限(即,我无法添加虚拟方法等)

作为一个具体示例,让我们考虑一下看起来像这样的AST类结构:

class ASTNode {}; // this one is fully abstract; i.e. there's a virtual void method() = 0;

class Assignment : ASTNode {};
class Expression : ASTNode {};

class StringExpr : Expression {};
class MathExpr : Expression {};

我想编写一个函数act,该函数将ASTNode的一个实例作为参数,并根据其实际的动态类型执行不同的操作。 通话将是这样的

std::shared_ptr<ASTNode> parsedAST = get_a_parsed_ASTNode(); // ... received from some parser or library
act(parsedAST);

然后,我想采取行动,具体取决于ASTNode的动态类型。

void act(std::shared_ptr<MathExpr> expr)
{
  // Do something with Math expressions, e.g. evaluate their value
};

void act(std::shared_ptr<StringExpr> expr)
{
  // Do something with String  expressions, e.g. write their value to the log
};

void act(std::shared_ptr<Expression> expr)
{
  // do something with other types of expressions (e.g. Boolean expressions)
}; 

但是,由于它们的动态类型可能不是``最具体的类型'',因此目前无法调用。取而代之的是,我必须手动按如下方式手动创建一个调度程序,但是我认为该方法有点愚蠢,因为它实际上除了调度之外什么也没做。

void act(std::shared_ptr<ASTNode> node_ptr)
{
  if(std::shared_ptr<MathExpr> derived_ptr = std::dynamic_pointer_cast<MathExpr>(node_ptr))
  {
    act(derived_ptr);
  }
  else if(std::shared_ptr<StringExpr> derived_ptr = std::dynamic_pointer_cast<StringExpr>(node_ptr))
  {
    act(derived_ptr);
  }
  else if(std::shared_ptr<Expression> derived_ptr = std::dynamic_pointer_cast<Expression>(node_ptr))
  {
     // do something with generic expressions. Make sure that this is AFTER the more concrete if casts
  }
  else if( ... )  // more of this
  {
     
  }
  // more else if
  else
  {
     // default action or raise invalid argument exception or so...
  }
};

这特别烦人且容易出错,因为我的类层次结构具有许多(> 20)不同的可实例化的具体类。另外,我有各种act函数,当我重构事物时(例如,将act添加为其他类型),我必须确保注意{{1}的正确顺序}内。 同样,它也不是那么稳定,因为基础类层次结构的更改将要求我直接更改每个调度程序,而不仅仅是更改特定的if(dynamic_pointer_cast)函数。

是否有更好/更智能的解决方案?显然,我很喜欢“本机”解决方案,但我也愿意考虑使用库。

1 个答案:

答案 0 :(得分:0)

我自己从未遇到过此类问题,但可以想到以下解决方案。

创建一个模仿原始层次结构的层次结构,该层次结构具有虚拟act,基址具有基本指针,然后将其强制转换为相应的派生指针。

现在,要创建所需的包装,您不需要正确排序的dynamic_cast,只需分配typeid字符串即可。因此,您的调度是从字符串到包装器工厂的映射。

当然,typeid字符串需要RTTI,但dynamic_cast也需要RTTI。