The visitor pattern gets slagged on a bit too much than it deserves. It's a tool, and it may be a substantial improvement (depending on how deep you are when you reach for it).
In the course of reworking some legacy code, I had to explain the visitor pattern to my teammates, and I thought the best way to explain it would be to write a little visitor pattern code generator.
(Apologies for the terrible style, I was in a hurry).
#include <assert.h>
#include <fstream>
#include <iostream>
#include <sstream>
#include <string>
#include <vector>
// prints to out something like "a.o b.o ";
void objects(std::ostream& out, std::vector<std::string> enumvalues) {
for (std::vector<std::string>::iterator i = enumvalues.begin();
i != enumvalues.end();
++i) {
out << *i << ".o ";
}
}
// prints to out a Makefile rule for building a static library
void libraryrule(std::ostream& out, std::string enumname, std::vector<std::string> enumvalues) {
out << enumname << ".a: ";
objects(out, enumvalues);
out << "\n";
out << "\tar cr " << enumname << ".a ";
objects(out, enumvalues);
out << "\n";
}
// prints to out a Makefile rule for building an object
void object(std::ostream& out, std::string objectname, std::string enumname) {
out << objectname << ".o: "
<< objectname << ".cpp "
<< objectname << ".h "
<< enumname << ".h "
<< enumname << "_visitor.h\n";
out << "\tg++ -c " << objectname << ".cpp\n";
}
// creates a list of Makefile rules to compile objects
void objectrules(std::ostream& out, std::string enumname, std::vector<std::string> enumvalues) {
for (std::vector<std::string>::iterator i = enumvalues.begin();
i != enumvalues.end();
++i) {
if (i != enumvalues.begin()) {
out << "\n";
}
object(out, *i, enumname);
}
}
void makefile(std::string enumname, std::vector<std::string> enumvalues) {
std::ofstream makefile("Makefile");
libraryrule(makefile, enumname, enumvalues);
makefile << "\n";
objectrules(makefile, enumname, enumvalues);
}
void a_cppfile(std::string objectname, std::string enumname) {
std::string filename = objectname + ".cpp";
std::ofstream out(filename.c_str());
out << "#include \"" << objectname << ".h\"\n";
out << "#include \"" << enumname << "_visitor.h\"\n";
out << "\n";
out << "void " << objectname << "::accept(" << enumname << "Visitor& v) {\n";
out << " v.visit" << objectname << "(*this);\n";
out << "}\n";
}
void cppfiles(std::string enumname, std::vector<std::string> enumvalues) {
for (std::vector<std::string>::iterator i = enumvalues.begin();
i != enumvalues.end();
++i) {
a_cppfile(*i, enumname);
}
}
void a_hfile(std::string objectname, std::string enumname) {
std::string filename = objectname + ".h";
std::ofstream out(filename.c_str());
out << "#ifndef " << objectname << "_H\n";
out << "#define " << objectname << "_H\n";
out << "\n";
out << "#include \"" << enumname << ".h\"\n";
out << "\n";
out << "class " << objectname << " : public " << enumname << " {\n";
out << " public:\n";
out << " void accept(" << enumname << "Visitor&);\n";
out << "};\n";
out << "\n";
out << "#endif\n";
}
void hfiles(std::string enumname, std::vector<std::string> enumvalues) {
for (std::vector<std::string>::iterator i = enumvalues.begin();
i != enumvalues.end();
++i) {
a_hfile(*i, enumname);
}
}
void interfacefile(std::string enumname) {
std::string filename = enumname + ".h";
std::ofstream out(filename.c_str());
out << "#ifndef " << enumname << "_H\n";
out << "#define " << enumname << "_H\n";
out << "\n";
out << "// forward declaration\n";
out << "class " << enumname << "Visitor;\n";
out << "\n";
out << "class " << enumname << " {\n";
out << " public:\n";
out << " virtual void accept(" << enumname << "Visitor&) = 0;\n";
out << "\n";
out << " // Destructor\n";
out << " virtual ~" << enumname << "() {}\n";
out << " protected:\n";
out << " // Constructor\n";
out << " " << enumname << "() {}\n";
out << "};\n";
out << "\n";
out << "#endif\n";
}
void visitorfile(std::string enumname, std::vector<std::string> enumvalues) {
std::string filename = enumname + "_visitor.h";
std::ofstream out(filename.c_str());
out << "#ifndef " << enumname << "_VISITOR_H\n";
out << "#define " << enumname << "_VISITOR_H\n";
out << "\n";
out << "// forward declarations\n";
for (std::vector<std::string>::iterator i = enumvalues.begin();
i != enumvalues.end();
++i) {
out << "class " << *i << ";\n";
}
out << "\n";
out << "class " << enumname << "Visitor {\n";
out << " public:\n";
for (std::vector<std::string>::iterator i = enumvalues.begin();
i != enumvalues.end();
++i) {
out << " virtual void visit" << *i << "(" << *i << "&) = 0;\n";
}
out << "\n";
out << " protected:\n";
out << " // Constructor\n";
out << " " << enumname << "Visitor() {};\n";
out << " // Destructor\n";
out << " ~" << enumname << "Visitor() {};\n";
out << "};\n";
out << "\n";
out << "#endif\n";
}
std::vector<std::string> read_one_word_per_line(std::istream& in) {
std::vector<std::string> accum;
std::string line;
while (std::getline(in, line)) {
std::stringstream line_stream(line);
std::string word;
assert(line_stream >> word);
accum.push_back(word);
}
return accum;
}
int main(int argc, char* argv[]) {
if (argc != 2) {
std::cout << "usage: " << argv[0] << " COLOR <colorlist.txt\n";
return 1;
}
std::string enumname(argv[1]);
std::vector<std::string> enumvalues = read_one_word_per_line(std::cin);
makefile(enumname, enumvalues);
cppfiles(enumname, enumvalues);
hfiles(enumname, enumvalues);
interfacefile(enumname);
visitorfile(enumname, enumvalues);
return 0;
}