vendredi 26 juin 2020

C++ Dynamic Dispatch Function

I'm trying to create an overloaded function that will be called with the dynamic type of an object. I try to do this without interfering with the actual class structure underneath, as I don't have direct access (i.e. I cannot add virtual methods, etc.)

As a concrete example, let's think of an AST class structure that looks somewhat like this:

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 {};

I want to write a function act that will take an instance of ASTNode as parameter and, depending on its actual dynamic type do something different. The call will be something like this

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

Then, I want to act, depending on the dynamic type of the 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)
}; 

Currently though, I cannot call since they dynamic type will be maybe not the ``most concrete type''. Instead, I have to manually create a dispatcher manually as follows, but the method is a bit silly in my opinion, since it does literally nothing else but dispatch.

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...
  }
};

This is especially annoying & error-prone since my class hierarchy has many (> 20) different concrete classes that can be instantiated. Also, I have various act-functions, and when I refactor things (e.g. add an act for an additional type), I have to make sure to pay attention to the correct order of if(dynamic_pointer_cast) within the dispatcher. Also it's not that stable, since a change in the underlying class hierarchy will require me to change every dispatcher directly, rather than just the specific act functions.

Is there a better / smarter solution? Evidently I'd appreciate "native" solutions, but I'm willing to consider libraries too.

Aucun commentaire:

Enregistrer un commentaire