-1

I am writing an Interpreter in c++ as a course work at the uni. Basically i'm translating this python interpreter to c++ by myself using google.

using visitor for an interpreter I have 2 classes

BinOp : public AST

Number : public AST

I have 2 methods in my Interpreter class

class Interpreter : public NodeVisitor

int visitBinOp(BinOp* node)
{
  //example if the operation is +
  //return this->visit(node->left) + this->visit(node->right)
}
int visitNumber(Number* node)
{
  //returns the int value that's in the node.
  //return node->value;
}

and 1 method in NodeVisitor that Interpreter inherits

class NodeVisitor
int visit(AST* node)
{

  //if node is BinOp properFunction is pointer to visitBinOp

  //if node is Number properFunction is pointer tp visitNumber

  //return properFunction(node)
}

Question 1:Whats the best way to check if AST is BinOp or Number

if(typeid(node) == typeid(BinOp*)

or through some casting (when trying dynamic_cast I get error that classes arent polymorphic).

Main Problem: I need to somehow to create a pointer to those functions but don't know how to.

EDIT 1 Added this code to NodeVisitor but due to it including "Interpreter.h" and Interpreter including "NodeVisitor.h" i get

error C2504: 'NodeVisitor' : base class undefined.

unsigned long int NodeVisitor::visit(AST* node)
{
  std::function<unsigned long int(Number* node)> visitNumber = std::bind(&Interpreter::VisitNumber);
  std::function<unsigned long int(BinaryOperation* node)> visitBinOp = std::bind(&Interpreter::VisitBinOp);
  if (typeid(node) == typeid(Number*))
  {
    visitNumber((Number*)node);
  }
  if (typeid(node) == typeid(BinaryOperation*))
  {
    visitBinOp((BinaryOperation*)node);
  }
}

I think i need to add extern "C" to visitBinOp and visitNumber funcs and use this approach, mentioned in here

void *handle = dlsym(0, RTLD_LOCAL | RTLD_LAZY);
FunctionType *fptr = (FunctionType *)dlsym(handle, "visitBinOp/visitNumber");
fptr();

But im not quite sure how this works.

  • if you use smart pointers you can use `std::dynamic_pointer_cast` – samuelnj Jan 18 '19 at 22:12
  • 2
    If you get an error that the class isn't polymorphic you probably need to add a virtual function (such as the destructor at minimum). But you're probably going the wrong way about this. You should have a virtual function that you can call that each derived class implements. There should be no reason to know the type of the node. – Kevin Jan 18 '19 at 22:15
  • 1
    *I need to somehow to create a pointer to those functions but don't know how to* Seek ye `std::function` and perhaps *lambda expressions*. – user4581301 Jan 18 '19 at 22:18
  • 1
    For a moment there, I misread the beginning as trying to write a C++ interpreter. Which would be a significant challenge. – Eljay Jan 18 '19 at 22:22
  • I once wrote answers with small sample interpreters in C++. May be, this can provide some additional inspiration: [SO: The Tiny Calculator Project – (recursive descent parser)](https://stackoverflow.com/a/46965151/7478597) and [SO: Small Parser from Syntax Diagram](https://stackoverflow.com/a/50021308/7478597). Concerning the former, have a look at the `virtual double solve()` method which every derived `AST::Expr` provides. May be, this is the missing piece you are looking for. – Scheff's Cat Jan 19 '19 at 09:53

2 Answers2

0

Note: While sometimes there may be a need to dynamic_cast<>, be aware that use of it is a "code smell".

In Object-oriented programming, you usually don't ask an object for its type and then do something based on that information. You tell the object to do what you need done, and it will do the right thing depending on what object it is.

So why not just give AST a virtual unsigned long InterpreterVisit(Interpreter* interpreter) method, and make

unsigned long Interpreter::visit(AST *node) {
    node->InterpreterVisit(this);
}

instead? If you want to keep the interpreter code separate from the AST code, you could also implement it as an attachment, where you have a second object owned by the AST node that implements InterpreterVisit(), and then you just need one spot that creates the correct type of attachment object for this type (E.g. using partial template specialization).

I don't know enough about the structure of your interpreter to say whether that works for your design, but I'd like to encourage you to pause and think whether there's a better way than dynamic_cast<> whenever you feel tempted to use it.

Sometimes it can also help to just "hide away" the use of the cast, especially if you find yourself doing the same cast a lot. Do it once, ahead of time. Attach an object to the objects based on that cast, then just call that object instead of casting over and over again.

PS: Regarding your circular include, sometimes you can avoid such situations. Several tools exist:

  1. Forward-declare one of the classes involved using class Foo; (note the semicolon) instead of including its header. This will tell C++ that a class of that name exists without pulling in the whole header and its use of the other class. You can't declare subclasses of a forward-declared class, but you can declare references and pointers to it.

  2. Split up your headers. Usually each class gets its own header (.h/.hpp) and implementation file (.cp/.cpp). That way, you can include only the classes you need, and forward-declare these classes in the headers of the others (and then only actually include the full header in the implementation files that use these classes).

  3. Split up your classes themselves. I.e. have one class MixinA with parts class A needs to include, another class MixinB with parts class B needs to include, and then create class C : public MixinA, public MixinB .... That way you have a class that does both, yet avoid the circle because only C gets to see the whole picture.

uliwitness
  • 7,806
  • 31
  • 50
  • [This](https://github.com/spider9375/Uni-Data-Structures/tree/master/Project/Interpretator) is my code. Interpreter takes a AST (abstract syntax tree) from the Parser and depending on the type, calls the corresponding functions. I have 7 ASTs and i cant make 7 different Parse() functions that the Parser returns to the Interpreter. – Dimitar Mihaylov Jan 24 '19 at 16:14
-1

Ok this morning i woke up with the answer. Instead of implementing the visit function in NodeVisitor.cpp, where i can't access the Interpreter functions, i made the visit function virtual and implemented it in the Interpreter.cpp

unsigned long int Interpreter::visit(AST* node)
{
  Number* number = dynamic_cast<Number*>(node);
  BinaryOperation* binOp = dynamic_cast<BinaryOperation*>(node);

  if (number)
  {
    return this->VisitNumber((Number*)node);
  }

  return this->VisitBinOp((BinaryOperation*)node);
}

I guess my brain just needed some time off... Been coding at work for 8 hours and then at home for 4 thats 12 hours straight :D