Visitor Pattern for AST - c++

I'm trying to use the visitor pattern to perform operations for the AST of my compiler but I can't seem to figure out an implementation that will work properly.
AST classes excerpt:
class AstNode
{
public:
AstNode() {}
};
class Program : public AstNode
{
public:
std::vector<std::shared_ptr<Class>> classes;
Program(const std::vector<std::shared_ptr<Class>>&);
void accept(AstNodeVisitor& visitor) const { visitor.visit(*this); }
};
class Expression : public AstNode
{
public:
Expression() {}
};
class Method : public Feature
{
public:
Symbol name;
Symbol return_type;
std::vector<std::shared_ptr<Formal>> params;
std::shared_ptr<Expression> body;
Method(const Symbol&, const Symbol&, const std::vector<std::shared_ptr<Formal>>&,
const std::shared_ptr<Expression>&);
feature_type get_type() const;
};
class Class : public AstNode
{
public:
Symbol name;
Symbol parent;
Symbol filename;
std::vector<std::shared_ptr<Feature>> features;
Class(const Symbol&, const Symbol&, const Symbol&,
const std::vector<std::shared_ptr<Feature>>&);
};
class Assign : public Expression
{
public:
Symbol name;
std::shared_ptr<Expression> rhs;
Assign(const Symbol&, const std::shared_ptr<Expression>&);
};
Visitor (partial implementation):
class AstNodeVisitor
{
public:
virtual void visit(const Program&) = 0;
virtual void visit(const Class&) = 0;
virtual void visit(const Attribute&) = 0;
virtual void visit(const Formal&) = 0;
virtual void visit(const Method&) = 0;
};
class AstNodePrintVisitor : public AstNodeVisitor
{
private:
size_t depth;
public:
void visit(const Program& node) {
for (auto cs : node.classes)
visit(*cs);
}
void visit(const Class&);
void visit(const Attribute&);
void visit(const Formal&);
void visit(const Method&);
};
How I'm using it:
AstNodePrintVisitor print;
ast_root->accept(print); // ast_root is a shared_ptr<Program>
The issue:
The Method Node contains a body member of type Expression - which is a base class. How will I visit it?
I thought maybe I could simply write an accept method for each AST node and do the traversal there instead. (ie. instead of calling visit() in the visitor, call accept() in the visitable then call visit(*this) so the calls will be polymorphic and the right visit() method of the visitor gets called.
However, if I do this, I will have no option for traversing top-down (operation then recurse) or bottom-up (recurse then operation) since I have to choose only one. By this I mean a PrintVisitor for example will need a top-down traversal of the AST but a TypeCheck will need a bottom-up approach.
Is there a way around this? Or am I over-engineering things? Right now I think the fastest way is to just implement the methods in the nodes themselves.

Let's begin with a minor correction to the craft of a Visitor:
void visit(const Program& node) {
for (auto cs : node.classes)
visit(*cs);
}
the call visit(*cs) should be cs->accept(*this) to allow for virtual dispatch, in the generic case.
And now to the main question: the control of traversal order.
A visitor can only really visit a tree in a depth first way, breadth first may be implemented but is quirky in a single visit method (you basically need to separate visitation from iterations on children).
On the other hand, even in a depth first traversal, you may chose whether to act on the parent either before or after having visited the children.
The typical way to do so would be to provide an intermediate layer between the pure base class and the real actor, for example:
class RecursiveAstNodeVisitor: public AstNodeVisitor
{
public:
// returns whether or not to stop recursion
virtual bool actBefore(Program const&) { return false; }
virtual void actAfter(Program const&) {}
virtual bool actBefore(Class const&) { return false; }
virtual void actAfter(Class const&) {}
// ... You get the idea
virtual void visit(Program const& p) {
if (actBefore(p)) { return; }
for (auto c: p.classes) {
c->accept(*this);
}
actAfter(p);
}
// ... You get the idea
};
The overrider is free to act either before or after the recursion occurs... and of course may act on both!
class PrintAstNodeVisitor: public RecursiveAstNodeVisitor {
public:
PrintAstNodeVisitor(std::ostream& out): _out(out), _prefix() {}
virtual bool actBefore(Program const& p) {
_out << "{\n";
_out << " \"type\": \"Program\",\n";
_out << " \"name\": \" << p.name << "\",\n";
_out << " \"classes\": [\n";
_prefix = " ";
return false;
}
virtual void actAfter(Program const& p) {
_out << " ]\n";
_out << "}\n";
}
virtual bool actBefore(Class const& c) {
_out << _prefix << "{\n";
_out << _prefix << " \"type\": \"Class\",\n";
// ...
}
private:
std::ostream& _out;
std::string _prefix;
};

Related

Mix template and non-template visitor methods

Currently I am learning about the Visitor Pattern and try out various ideas.
Below I have the code of my current setup, which I would like to get functioning somehow.
I would like to have two visitors, one that counts instances of Red and Blu separately and one that counts anything (one can assume, it's a Color)
This is of course solvable by simply implementing the second visitor analogously to the first one, however not using separate variables for counting, but just one.
I think however this is unnecessary - if I had for example many, many different colours, the code would be very repetitive: All functions in that visitor would be same, they would simply increment one variable. Surely, there is an easier way, but how?
According to the standard Visitor Pattern I have to implement for every color class a visit functions, thus this does not seem to be the right approach.
How would someone solve this problem?
#include <iostream>
class Color
{
public:
virtual void accept(class Visitor*) = 0;
};
class Red: public Color
{
public:
/*virtual*/
void accept(Visitor*);
void eye()
{
std::cout << "Red::eye\n";
}
};
class Blu: public Color
{
public:
/*virtual*/
void accept(Visitor*);
void sky()
{
std::cout << "Blu::sky\n";
}
};
class Visitor
{
public:
virtual void visit(Red*) = 0;
virtual void visit(Blu*) = 0;
};
class CountVisitor: public Visitor
{
public:
CountVisitor()
{
m_num_red = m_num_blu = 0;
}
/*virtual*/
void visit(Red*)
{
++m_num_red;
}
/*virtual*/void visit(Blu*)
{
++m_num_blu;
}
void report_num()
{
std::cout << "Reds " << m_num_red << ", Blus " << m_num_blu << '\n';
}
private:
int m_num_red, m_num_blu;
};
class TemplateVisitor: public Visitor
{
public:
TemplateVisitor() : num_of_colours(0) {}
/*virtual*/
template<class C>
void visit(C* c)
{
++num_of_colours;
}
void report_num()
{
std::cout << "Colours " << num_of_colours << '\n';
}
private:
int num_of_colours;
};
void Red::accept(Visitor *v)
{
v->visit(this);
}
void Blu::accept(Visitor *v)
{
v->visit(this);
}
int main()
{
Color *set[] =
{
new Red, new Blu, new Blu, new Red, new Red, nullptr
};
CountVisitor count_operation;
TemplateVisitor template_visitor;
for (int i = 0; set[i]; i++)
{
set[i]->accept(&count_operation);
set[i]->accept(&template_visitor);
}
count_operation.report_num();
template_visitor.report_num();
}
Unfortunately, virtual methods and template methods can't match.
I mean... if your base class Visitor require
virtual void visit(Red*) = 0;
virtual void visit(Blu*) = 0;
the implementation of two virtual methods in derived classes, you can't solve this obligation with a single template method
template<class C>
void visit(C* c)
{
++num_of_colours;
}
You have to write two methods, absolutely not template, with the exact signature. Maybe adding also override, to reduce the risk of mistakes.
void visit (Red * r) override
{ ++num_of_colours; }
void visit (Blu * b) override
{ ++num_of_colours; }
Obviously you can define a template method (maybe with another name, but also visit() if you want) that is called by both virtual overrided methods
template <typename C>
void visit (C * c)
{ ++num_of_colours; }
void visit (Red * r) override
{ visit<Red>(r); }
void visit (Blu * b) override
{ visit<Blu>(b); }
This way, you can implement the logic of the visitor in a single template method and call it by all virtual methods
Why not just use a map and add some function to color to use as an identifier?
class Color
{
public:
virtual void accept(class Visitor*) = 0;
virtual std::string color_name() = 0;
};
class Visitor
{
public:
virtual void visit(Color* c);
};
class CountVisitor: public Visitor
{
std::unordered_map<std::string, int> map;
public:
/*virtual*/
void visit(Color* c)
{
map[c.color_name()]++;
}
};

C++ Visitor pattern with smart pointers

I am trying to implement Oppen's algorithm in C++.
The basic routines in this algorithm (print and scan) dispatch on a token type.
It seems natural to implement this dispatch using the visitor pattern.
The problem is: the routines are nested and the arguments to print() are enqueued in a stack during scan().
In order to avoid any memory problems, I would like to use smart pointers for the task.
So my implementation looks like this:
class Text;
class Line;
class Open;
class Close;
class Visitor {
/* Define virtual visit functions for concrete doc nodes:
*/
public:
virtual void visit(const Text&) = 0;
virtual void visit(const Line&) = 0;
virtual void visit(const Open&) = 0;
virtual void visit(const Close&) = 0;
};
class DocToken
{
protected:
explicit DocToken() {}
friend class Visitor;
public:
virtual void accept(Visitor * visitor) const = 0;
};
class Text : public DocToken {
public:
Text(std::string s) : text(s) {}
void accept(Visitor *visitor) const {
visitor -> visit (*this);
}
std::string text;
};
class Open : public DocToken { /* .. */ }
/* .. */
class Scan : public Visitor {
stream_t stream;
/* ... */
public:
void visit(const Open& x) {
/* ... */
stream.push_back(/* .. */ new Open() /* .. */);
/* ... */
}
void visit(const Text& x) {
/* ... */
stream.push_back(/* .. */ new Text(x) /* .. */);
/* ... */
}
/* .. */
}
As you can see, the Open token does not carry any data and can be constructed in place easily. The Text token does carry data (a std::string) and has to be copied in order to be pushed into the stream.
The stream needs to consist of pointers due to the common abstract base class of Open and Text.
Since on the outside, there is a smart pointer to that text token, I'd like to avoid the copying and simply use the existing smart pointer.
However, the accept method does not have access to that smart pointer.
Is there a way to implement a visitor pattern directly on smart-pointers? If not, how can I reduce the cost of copying the text token?
Technically, You can do this using std::enable_shared_from_this. (Note Pete Kirkham's excellent comment to the question, though - shared pointers indicate ownership. This is applicable to visitors that might outlive their originating documents, e.g., an ad-hoc dictionary builder, which might live after the document has been closed. Where no ownership is involved, raw pointers are the way to go.)
Below is a simplified version of your code illustrating this.
Say we start with the usual visitor-pattern forward declarations and base class definitions.
#include <memory>
#include <vector>
#include <iostream>
struct token;
struct visitor;
struct token {
virtual void accept(visitor &v) = 0;
};
struct text_token;
struct open_token;
When we define visitor, we make it accept std::shared_ptrs of the options:
struct visitor {
virtual void accept(std::shared_ptr<text_token> p) = 0;
virtual void accept(std::shared_ptr<open_token> p) = 0;
};
Now when we make concrete tokens, we:
subclass std::enable_shared_from_this
use shared_from_this to pass on the argument to accept
so the concrete tokens become:
struct text_token : public token, public std::enable_shared_from_this<text_token> {
virtual void accept(visitor &v) override {
std::shared_ptr<text_token> p{shared_from_this()};
v.accept(p);
}
};
struct open_token : public token, public std::enable_shared_from_this<open_token> {
virtual void accept(visitor &v) override {
std::shared_ptr<open_token> p{shared_from_this()};
v.accept(p);
}
};
The concrete visitor doesn't change by much:
struct scan : public visitor {
virtual void accept(std::shared_ptr<text_token>) override {
std::cout << "accepting text" << std::endl;
}
virtual void accept(std::shared_ptr<open_token>) override {
std::cout << "accepting open" << std::endl;
}
};
Now we can define a range of std::shared_ptrs to tokens
int main() {
std::vector<std::shared_ptr<token>> toks;
toks.push_back(std::make_shared<text_token>());
toks.push_back(std::make_shared<open_token>());
And call accept on them:
scan s;
for(auto p: toks)
p->accept(s);
}
When run, it prints:
$ ./a.out
accepting text
accepting open
Full Code
#include <memory>
#include <vector>
#include <iostream>
struct token;
struct visitor;
struct token {
virtual void accept(visitor &v) = 0;
};
struct text_token;
struct open_token;
struct visitor {
virtual void accept(std::shared_ptr<text_token> p) = 0;
virtual void accept(std::shared_ptr<open_token> p) = 0;
};
struct text_token : public token, public std::enable_shared_from_this<text_token> {
virtual void accept(visitor &v) override {
std::shared_ptr<text_token> p{shared_from_this()};
v.accept(p);
}
};
struct open_token : public token, public std::enable_shared_from_this<open_token> {
virtual void accept(visitor &v) override {
std::shared_ptr<open_token> p{shared_from_this()};
v.accept(p);
}
};
struct scan : public visitor {
virtual void accept(std::shared_ptr<text_token>) override {
std::cout << "accepting text" << std::endl;
}
virtual void accept(std::shared_ptr<open_token>) override {
std::cout << "accepting open" << std::endl;
}
};
int main() {
std::vector<std::shared_ptr<token>> toks;
toks.push_back(std::make_shared<text_token>());
toks.push_back(std::make_shared<open_token>());
scan s;
for(auto p: toks)
p->accept(s);
}

visitor pattern for template derived classes

Related question: link.
In one of the answers to the question above, I was recommended to use the visitor pattern to resolve some of the issues with my class inheritance structure. However, I am not sure if it is possible to use it in my context because my derived classes can be non-type templates.
To showcase the problem I used a modified code from this source: http://sourcemaking.com/design_patterns/visitor/cpp/2.
The example below does not compile because it is not possible to define a virtual template method. However, I believe, the code demonstrates what I am trying to achieve. Are there any alternatives solutions to the problem?
// 1. Add an accept(Visitor) method to the "element" hierarchy
class Element
{
public:
virtual void accept(class Visitor &v) = 0;
};
template <unsigned int N>
class This: public Element
{
public:
/*virtual*/void accept(Visitor &v);
string thiss()
{
return "This";
}
};
class That: public Element
{
public:
/*virtual*/void accept(Visitor &v);
string that()
{
return "That";
}
};
// 2. Create a "visitor" base class w/ a visit() method for every "element" type
class Visitor
{
public:
template<unsigned int N>
virtual void visit(This<N> *e) = 0;
virtual void visit(That *e) = 0;
};
template<unsigned int N>
/*virtual*/void This<N>::accept(Visitor &v)
{
v.visit(this);
}
/*virtual*/void That::accept(Visitor &v)
{
v.visit(this);
}
// 3. Create a "visitor" derived class for each "operation" to do on "elements"
class UpVisitor: public Visitor
{
/*virtual*/void visit(This *e)
{
cout << "do Up on " + e->thiss() << '\n';
}
/*virtual*/void visit(That *e)
{
cout << "do Up on " + e->that() << '\n';
}
};
class DownVisitor: public Visitor
{
/*virtual*/void visit(This *e)
{
cout << "do Down on " + e->thiss() << '\n';
}
/*virtual*/void visit(That *e)
{
cout << "do Down on " + e->that() << '\n';
}
};
int main()
{
Element *list[] =
{
new This<3>(), new That()
};
UpVisitor up; // 4. Client creates
DownVisitor down; // "visitor" objects
for (int i = 0; i < 2; i++) list[i]->accept(up);
for (int i = 0; i < 2; i++) list[i]->accept(down);
}
The problem is your Visitor class is tightly coupled with classes that derive from Element. As you expand your design this is going to get in the way more than it already is. You can reduce/eliminate the right coupling by providing a "destination" class that defines all the requirements of a visitable object. Since the name of a derived classes is a common attribute you can place the storage and access to it into the destination class as well.
// 1. Define out visitor and destination interfaces
struct Destination
{
Destination(const std::string& name) : name_(name) {}
virtual std::string ident() const { return name_; }
const std::string name_;
};
struct Visitor
{
virtual void visit(Destination *e) = 0;
};
This keeps the requirements of the visitor separate from the Element class which seems to be your intention. Then your This and That classes inherit from Destination and provide the necessary implementations.
// 2. Define our element and it's derived classes
class Element
{
public:
virtual void accept(class Visitor &v) = 0;
};
template <unsigned int N>
class This: public Element, public Destination
{
public:
This() : Destination("This") {}
virtual void accept(Visitor &v)
{
v.visit(this);
}
};
class That: public Element, public Destination
{
public:
That() : Destination("That") {}
virtual void accept(Visitor &v)
{
v.visit(this);
}
};
Now your up and down visitors are simplified into something like the following
// 3. Create a "visitor" derived class for each "operation" to do on "elements"
class UpVisitor: public Visitor
{
void visit(Destination *e) {
cout << "do Up on " + e->ident() << '\n';
}
};
class DownVisitor: public Visitor
{
void visit(Destination *e) {
cout << "do Down on " + e->ident() << '\n';
}
};
Although I did not change it in the solution above I recommend changing visit to take a reference instead of a pointer. Since C++ has no notion of a null reference this indicates that Destination is required where as a pointer could be considered optional.

how to call member function of a derived class without downcasting

I have a hirerchy of Message class and Processor class. Each processor can recieve one or more messages on the fly. As each message can have some differnt attributes, I've to downcast that message to the concrect message class, to actually process that.
As there are a no. of message classes and process classes, I don't want to use dynamic_cast.
I tried to use following code, but this is giving compile time error.
Also, I have the flexibility to attach a processor pointer with a message (if needed), but not the other way round.
class Message
{
public:
virtual const Message* const getMessage() const = 0;
};
class MA : public Message
{
public:
const MA* const getMessage() const {return this;}
void printMA() const{std::cout<<"I am MA"<<std::endl;}
};
class MB : public Message
{
public:
const MB* const getMessage() const {return this;}
void printMB() const{std::cout<<"I am MB"<<std::endl;}
};
class Processor
{
public:
virtual void process(const Message* m) = 0;
};
class PA : public Processor
{
public:
void process(const Message* m) {processM(m->getMessage());}
void processM(const MA* m) {m->printMA();}
void processM(const MB* m) {m->printMB();}
};
int main()
{
Message* m1 = new MA();
Message* m2 = new MB();
Processor* p1 = new PA();
p1->process(m1);
p1->process(m2);
return 0;
}
I used 'double dispatch' finally to get around this. Now, the only thing is that I need to add a function in MessageProcessor' class, whenever i add a new message type., but i think that is fine.
class MessageProcessor
{
public:
virtual void process(const MA*) const{std::cout<<"unhandled:MA"<<std::endl;}
virtual void process(const MB*) const{std::cout<<"unhandled:MB"<<std::endl;}
virtual void process(const MC*) const{std::cout<<"unhandled:MC"<<std::endl;}
};
class Message
{
public:
virtual void process(const MessageProcessor*) const = 0;
};
class MA : public Message
{
public:
void printMA() const{std::cout<<"I am MA"<<std::endl;}
virtual void process(const MessageProcessor* p) const {p->process(this);}
};
class MB : public Message
{
public:
void printMB() const{std::cout<<"I am MB"<<std::endl;}
virtual void process(const MessageProcessor* p) const {p->process(this);}
};
class MC : public Message
{
public:
void printMC() const{std::cout<<"I am MC"<<std::endl;}
virtual void process(const MessageProcessor* p) const {p->process(this);}
};
class Processor : public MessageProcessor
{
public:
void processM(const Message* m){m->process(this);}
};
class PA : public Processor
{
public:
void process(const MA* m) const {m->printMA();}
void process(const MB* m) const {m->printMB();}
};
class PB : public Processor
{
public:
void process(const MA* m) const {m->printMA();}
void process(const MC* m) const {m->printMC();}
};
int main()
{
const Message* m1 = new MA();
const Message* m2 = new MB();
const Message* m3 = new MC();
Processor* p1 = new PA();
p1->processM(m1);
p1->processM(m2);
p1->processM(m3);
Processor* p2 = new PB();
p2->processM(m1);
p2->processM(m2);
p2->processM(m3);
return 0;
}
The most general solution to your problem is probably the Visitor pattern.
The simplest thing to do is eliminate the getMessage() method, and make the print() pure virtual in Message and override this in MA and MB. Furthermore, you can make process() a pure virtual method in Process and override this in PA. See code below:
#include <iostream>
class Message
{
public:
const std::string _id;
Message(std::string id):_id(id) {}
virtual void print() const = 0;
virtual void other_fun() const = 0;
};
class MA : public Message
{
private: double d_;
public:
MA():Message("MA"), d_(0.0) {}
virtual void print() const
{
std::cout<<"I am MA"<<std::endl;
std::cout << "I also have a double" << std::endl;
}
virtual void other_fun() const { std::cout << "I am MA specific" << std::endl; }
void do_hoops () const { std::cout << "Hoop!"<<std::endl;}
};
class MB : public Message
{
private: int i_;
public:
MB():Message("MB"), i_(0) {}
virtual void print() const
{
std::cout<<"I am MB"<<std::endl;
std::cout << "I also have an int"<<std::endl;
}
virtual void other_fun() const { std::cout << "I am MB specific" << std::endl; }
void do_twist() const { std::cout << "Twist!"<<std::endl; }
};
class Processor
{
public:
const std::string _id;
Processor(std::string id) : _id(id){}
virtual void process(const Message* m) = 0;
};
class PA : public Processor
{
public:
PA():Processor("PA") {}
virtual void process(const Message* m)
{
m->print();
m->other_fun();
}
};
int main()
{
Message* m1 = new MA();
Message* m2 = new MB();
// generic handling of message
Processor* p1 = new PA();
p1->process(m1);
p1->process(m2);
// message specific stuff
dynamic_cast<MA*>(m1)->do_hoops();
dynamic_cast<MB*>(m2)->do_twist();
return 0;
}
Output on Ideone.
No casts are required, the virtual functions will be selected at runtime through dynamic dispatch (virtual table lookup etc.). Message and Process are abstract base classes ("interfaces") and MA, MB and PA are concrete classes implementing these interfaces. Ideally, you also would factor the std::string state out of the Message interface, but that's left as an exercise.
Casting would be required if you would call functions that are specific to a derived class, and if you know at runtime that you are in fact calling such a class. This is done through a dynamic_cast to the particular derived class your base class pointer is currently pointing to.
You have a design flaw. Signature of Processor::process suggests it takes a Message, then it should not break this promise by trying to access something that is not a public interface of Message.
You can make Process a template class (host) that inherits from user supplied policies. Policies here are the concrete Message classes. Something like this:
#include <iostream>
struct MA
{
void print ()
{
std::cout << "MA: I'm the interface" << std::endl;
}
void printMA ()
{
std::cout << "MA: I'm special" << std::endl;
}
};
struct MB
{
void print ()
{
std::cout << "MB: I'm the interface" << std::endl;
}
void printMB ()
{
std::cout << "MB: I'm special" << std::endl;
}
};
template <typename M>
struct Process :
public M
{
void process()
{
M::print();
}
};
int main ()
{
Process<MA> p1;
Process<MB> p2;
p1.print(); // MA: I'm the interface
p1.printMA(); // MA: I'm special
p2.print(); // MB: I'm the interface
p2.printMB(); // MB: I'm special
}
Policies have print method that defines its interface. They also have some special methods like printMA and printMB. Host class (here Process) acts as user's interface to the policies. It can use the interface methods from policy classes. Special policy methods can be invoked by the user through host class.
You've run into a limitation of C++. What you really want is for the polymorphism to work on the arguments to a method, not just the method that the arguments are called on. It's generally referred to as double dispatch. Unfortunately, while there are some kind-of work-arounds, I haven't seen any perfect ones. That Wikipedia article shows the generally accepted workaround (using the Visitor pattern).

c++ double dispatch observer notification

Here is the code I am currently troubleshooting:
void CTimer::notify()
{
std::vector<IObserver*>::iterator it;
for(it=observers.begin();it!=observers.end();++it)
{
ITimerNotification* notification = new CTimerNotification(now());
(*it)->readNotification(*notification);
}
}
class CTimerNotification : public ITimerNotification
{
public:
CTimerNotification(const timeval& t)
{
time = t;
}
protected:
timeval time;
private:
virtual ~CTimerNotification();
virtual void read(const IObserver& o) const
{
o.update(*this);
}
virtual const timeval& getTime() const
{
return time;
}
};
class IObserver
{
public:
virtual ~IObserver();
virtual void readNotification(const INotification&) const=0;
virtual void update(const INotification&) const=0;
};
class ITimerObserver : public IObserver
{
public:
virtual void update(const ITimerNotification&) const=0;
};
class TestObserver : public ITimerObserver
{
public:
virtual void readNotification(const INotification& n) const
{
n.read(*this);
}
virtual void update(const INotification& n) const
{
std::cout<<"???: TestObserver: update()!\n";
}
virtual void update(const ITimerNotification& n) const
{
std::cout<< n.getTime().tv_sec << "." << n.getTime().tv_usec <<": TestObserver: update()!\n";
}
};
So the code runs, CTimer::notify() gets called, which creates a TimerNotification and passes it to the observer via readNotification() which in turn calls the notification's read() method, which finally calls the observer's (hopefully) correct update() method.
The last step is what fails. It calls the update(INotification&) method instead of the desired update(ITimerNotification&) method.
What am I missing here for this attempted Double Dispatch pattern to work? It does not appear to be getting the correct type information to select the appropriate function call.
Thanks for any help!
CTimerNotification needs a read something like this
virtual void read(const IObserver& o) const {
ITimerObserver* to = dynamic_cast<ITimerObserver*>(&o);
if (to) {
to->update(*this);
} else {
o.update(*this);
}
}
and you need a using IObserver::update; in ITimerObserver.